2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
33 * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
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
47 #include <sys/types.h>
48 #include <sys/socket.h>
50 #include <arpa/inet.h>
59 #include "closestream.h"
61 #include "pathnames.h"
71 #ifdef HAVE_LIBSYSTEMD
72 # define SD_JOURNAL_SUPPRESS_LOCATION
73 # include <systemd/sd-daemon.h>
74 # include <systemd/sd-journal.h>
77 #ifdef HAVE_SYS_TIMEX_H
78 # include <sys/timex.h>
84 ALL_TYPES
= TYPE_UDP
| TYPE_TCP
88 AF_UNIX_ERRORS_OFF
= 0,
94 OPT_PRIO_PREFIX
= CHAR_MAX
+ 1,
102 OPT_STRUCTURED_DATA_ID
,
103 OPT_STRUCTURED_DATA_PARAM
,
107 /* rfc5424 structured data */
108 struct structured_data
{
109 char *id
; /* SD-ID */
110 char **params
; /* array with SD-PARAMs */
112 struct list_head sds
;
118 pid_t pid
; /* zero when unwanted */
119 char *hdr
; /* the syslog header (based on protocol) */
123 char *unix_socket
; /* -u <path> or default to _PATH_DEVLOG */
127 size_t max_message_size
;
128 struct list_head user_sds
; /* user defined rfc5424 structured data */
129 struct list_head reserved_sds
; /* standard rfc5424 structured data */
131 void (*syslogfp
)(struct logger_ctl
*ctl
);
134 unix_socket_errors
:1, /* whether to report or not errors */
135 noact
:1, /* do not write to sockets */
136 prio_prefix
:1, /* read priority from input */
137 stderr_printout
:1, /* output message to stderr */
138 rfc5424_time
:1, /* include time stamp */
139 rfc5424_tq
:1, /* include time quality markup */
140 rfc5424_host
:1, /* include hostname */
141 skip_empty_lines
:1, /* do not send empty lines when processing files */
142 octet_count
:1; /* use RFC6587 octet counting */
145 #define is_connected(_ctl) ((_ctl)->fd >= 0)
146 static void logger_reopen(struct logger_ctl
*ctl
);
149 * For tests we want to be able to control datetime outputs
152 static inline int logger_gettimeofday(struct timeval
*tv
, struct timezone
*tz
)
154 char *str
= getenv("LOGGER_TEST_TIMEOFDAY");
157 if (str
&& sscanf(str
, "%ju.%ju", &sec
, &usec
) == 2) {
160 return tv
->tv_sec
>= 0 && tv
->tv_usec
>= 0 ? 0 : -EINVAL
;
163 return gettimeofday(tv
, tz
);
166 static inline char *logger_xgethostname(void)
168 char *str
= getenv("LOGGER_TEST_HOSTNAME");
169 return str
? xstrdup(str
) : xgethostname();
172 static inline pid_t
logger_getpid(void)
174 char *str
= getenv("LOGGER_TEST_GETPID");
177 if (str
&& sscanf(str
, "%u", &pid
) == 1)
183 #undef HAVE_NTP_GETTIME /* force to default non-NTP */
185 #else /* !TEST_LOGGER */
186 # define logger_gettimeofday(x, y) gettimeofday(x, y)
187 # define logger_xgethostname xgethostname
188 # define logger_getpid getpid
192 static int decode(const char *name
, const CODE
*codetab
)
194 register const CODE
*c
;
196 if (name
== NULL
|| *name
== '\0')
198 if (isdigit(*name
)) {
203 num
= strtol(name
, &end
, 10);
204 if (errno
|| name
== end
|| (end
&& *end
))
206 for (c
= codetab
; c
->c_name
; c
++)
211 for (c
= codetab
; c
->c_name
; c
++)
212 if (!strcasecmp(name
, c
->c_name
))
218 static int pencode(char *s
)
225 separator
= strchr(s
, '.');
228 facility
= decode(s
, facilitynames
);
230 errx(EXIT_FAILURE
, _("unknown facility name: %s"), s
);
234 level
= decode(s
, prioritynames
);
236 errx(EXIT_FAILURE
, _("unknown priority name: %s"), s
);
237 if (facility
== LOG_KERN
)
238 facility
= LOG_USER
; /* kern is forbidden */
239 return ((level
& LOG_PRIMASK
) | (facility
& LOG_FACMASK
));
242 static int unix_socket(struct logger_ctl
*ctl
, const char *path
, int *socket_type
)
244 int fd
= -1, i
, type
= -1;
245 static struct sockaddr_un s_addr
; /* AF_UNIX address of local logger */
247 if (strlen(path
) >= sizeof(s_addr
.sun_path
))
248 errx(EXIT_FAILURE
, _("openlog %s: pathname too long"), path
);
250 s_addr
.sun_family
= AF_UNIX
;
251 xstrncpy(s_addr
.sun_path
, path
, sizeof(s_addr
.sun_path
));
253 for (i
= 2; i
; i
--) {
256 if (i
== 2 && *socket_type
& TYPE_UDP
) {
260 if (i
== 1 && *socket_type
& TYPE_TCP
) {
264 if (st
== -1 || (fd
= socket(AF_UNIX
, st
, 0)) == -1)
266 if (connect(fd
, (struct sockaddr
*)&s_addr
, sizeof(s_addr
)) == -1) {
274 if (ctl
->unix_socket_errors
)
275 err(EXIT_FAILURE
, _("socket %s"), path
);
277 /* write_output() will try to reconnect */
281 /* replace ALL_TYPES with the real TYPE_* */
282 if (type
> 0 && type
!= *socket_type
)
287 static int inet_socket(const char *servername
, const char *port
, int *socket_type
)
289 int fd
, errcode
, i
, type
= -1;
290 struct addrinfo hints
, *res
;
291 const char *p
= port
;
293 for (i
= 2; i
; i
--) {
294 memset(&hints
, 0, sizeof(hints
));
295 if (i
== 2 && *socket_type
& TYPE_UDP
) {
296 hints
.ai_socktype
= SOCK_DGRAM
;
301 if (i
== 1 && *socket_type
& TYPE_TCP
) {
302 hints
.ai_socktype
= SOCK_STREAM
;
307 if (hints
.ai_socktype
== 0)
309 hints
.ai_family
= AF_UNSPEC
;
310 errcode
= getaddrinfo(servername
, p
, &hints
, &res
);
312 errx(EXIT_FAILURE
, _("failed to resolve name %s port %s: %s"),
313 servername
, p
, gai_strerror(errcode
));
314 if ((fd
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
)) == -1) {
318 if (connect(fd
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
329 errx(EXIT_FAILURE
, _("failed to connect to %s port %s"), servername
, p
);
331 /* replace ALL_TYPES with the real TYPE_* */
332 if (type
> 0 && type
!= *socket_type
)
337 #ifdef HAVE_LIBSYSTEMD
338 static int journald_entry(struct logger_ctl
*ctl
, FILE *fp
)
343 int n
, lines
= 0, vectors
= 8, ret
= 0, msgline
= -1;
346 iovec
= xreallocarray(NULL
, vectors
, sizeof(struct iovec
));
349 sz
= getline(&buf
, &dummy
, fp
);
351 (sz
= rtrim_whitespace((unsigned char *) buf
)) == 0) {
356 if (strncmp(buf
, "MESSAGE=", 8) == 0) {
358 msgline
= lines
; /* remember the first message */
360 char *p
= xrealloc(iovec
[msgline
].iov_base
,
361 iovec
[msgline
].iov_len
+ sz
- 8 + 2);
363 iovec
[msgline
].iov_base
= p
;
364 p
+= iovec
[msgline
].iov_len
;
366 memcpy(p
, buf
+ 8, sz
- 8);
369 iovec
[msgline
].iov_len
+= sz
- 8 + 1;
374 if (lines
== vectors
) {
376 if (IOV_MAX
< vectors
)
377 errx(EXIT_FAILURE
, _("maximum input lines (%d) exceeded"), IOV_MAX
);
378 iovec
= xreallocarray(iovec
, vectors
, sizeof(struct iovec
));
380 iovec
[lines
].iov_base
= buf
;
381 iovec
[lines
].iov_len
= sz
;
386 ret
= sd_journal_sendv(iovec
, lines
);
387 if (ctl
->stderr_printout
) {
388 for (n
= 0; n
< lines
; n
++)
389 fprintf(stderr
, "%s\n", (char *) iovec
[n
].iov_base
);
391 for (n
= 0; n
< lines
; n
++)
392 free(iovec
[n
].iov_base
);
398 /* this creates a timestamp based on current time according to the
399 * fine rules of RFC3164, most importantly it ensures in a portable
400 * way that the month day is correctly written (with a SP instead
401 * of a leading 0). The function uses a static buffer which is
402 * overwritten on the next call (just like ctime() does).
404 static char const *rfc3164_current_time(void)
406 static char time
[32];
410 static char const * const monthnames
[] = {
411 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
412 "Sep", "Oct", "Nov", "Dec"
415 ret
= logger_gettimeofday(&tv
, NULL
);
417 err(EXIT_FAILURE
, _("gettimeofday() failed"));
418 localtime_r(&tv
.tv_sec
, &tm
);
419 snprintf(time
, sizeof(time
),"%s %2d %2.2d:%2.2d:%2.2d",
420 monthnames
[tm
.tm_mon
], tm
.tm_mday
,
421 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
425 #define next_iovec(ary, idx) __extension__ ({ \
426 assert(ARRAY_SIZE(ary) > (size_t)idx); \
431 #define iovec_add_string(ary, idx, str, len) \
433 struct iovec *v = next_iovec(ary, idx); \
434 v->iov_base = (void *) str; \
435 v->iov_len = len ? len : strlen(str); \
438 #define iovec_memcmp(ary, idx, str, len) \
439 memcmp((ary)[(idx) - 1].iov_base, str, len)
441 /* writes generated buffer to desired destination. For TCP syslog,
442 * we use RFC6587 octet-stuffing (unless octet-counting is selected).
443 * This is not great, but doing full blown RFC5425 (TLS) looks like
444 * it is too much for the logger utility. If octet-counting is
445 * selected, we use that.
447 static void write_output(struct logger_ctl
*ctl
, const char *const msg
)
453 /* initial connect failed? */
454 if (!ctl
->noact
&& !is_connected(ctl
))
458 if (ctl
->octet_count
) {
459 size_t len
= xasprintf(&octet
, "%zu ", strlen(ctl
->hdr
) + strlen(msg
));
460 iovec_add_string(iov
, iovlen
, octet
, len
);
464 iovec_add_string(iov
, iovlen
, ctl
->hdr
, 0);
467 iovec_add_string(iov
, iovlen
, msg
, 0);
469 if (!ctl
->noact
&& is_connected(ctl
)) {
470 struct msghdr message
= { 0 };
471 #ifdef SCM_CREDENTIALS
472 struct cmsghdr
*cmhp
;
476 char control
[CMSG_SPACE(sizeof(struct ucred
))];
477 } cbuf
= { .control
= { 0 } };
480 /* 4) add extra \n to make sure message is terminated */
481 if ((ctl
->socket_type
== TYPE_TCP
) && !ctl
->octet_count
)
482 iovec_add_string(iov
, iovlen
, "\n", 1);
484 message
.msg_iov
= iov
;
485 message
.msg_iovlen
= iovlen
;
487 #ifdef SCM_CREDENTIALS
488 /* syslog/journald may follow local socket credentials rather
489 * than in the message PID. If we use --id as root than we can
490 * force kernel to accept another valid PID than the real logger(1)
493 if (ctl
->pid
&& !ctl
->server
&& ctl
->pid
!= getpid()
494 && geteuid() == 0 && kill(ctl
->pid
, 0) == 0) {
496 message
.msg_control
= cbuf
.control
;
497 message
.msg_controllen
= CMSG_SPACE(sizeof(struct ucred
));
499 cmhp
= CMSG_FIRSTHDR(&message
);
500 cmhp
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
501 cmhp
->cmsg_level
= SOL_SOCKET
;
502 cmhp
->cmsg_type
= SCM_CREDENTIALS
;
503 cred
= (struct ucred
*) CMSG_DATA(cmhp
);
505 cred
->pid
= ctl
->pid
;
508 /* Note that logger(1) maybe executed for long time (as pipe
509 * reader) and connection endpoint (syslogd) may be restarted.
511 * The libc syslog() function reconnects on failed send().
512 * Let's do the same to be robust. [kzak -- Oct 2017]
514 * MSG_NOSIGNAL is POSIX.1-2008 compatible, but it for example
515 * not supported by apple-darwin15.6.0.
518 # define MSG_NOSIGNAL 0
520 if (sendmsg(ctl
->fd
, &message
, MSG_NOSIGNAL
) < 0) {
522 if (sendmsg(ctl
->fd
, &message
, MSG_NOSIGNAL
) < 0)
523 warn(_("send message failed"));
527 if (ctl
->stderr_printout
) {
528 /* make sure it's terminated for stderr */
529 if (iovec_memcmp(iov
, iovlen
, "\n", 1) != 0)
530 iovec_add_string(iov
, iovlen
, "\n", 1);
532 ignore_result( writev(STDERR_FILENO
, iov
, iovlen
) );
539 static void syslog_rfc3164_header(struct logger_ctl
*const ctl
)
541 char pid
[30], *hostname
;
545 snprintf(pid
, sizeof(pid
), "[%d]", ctl
->pid
);
547 if ((hostname
= logger_xgethostname())) {
548 char *dot
= strchr(hostname
, '.');
552 hostname
= xstrdup(NILVALUE
);
554 xasprintf(&ctl
->hdr
, "<%d>%.15s %s %.200s%s: ",
555 ctl
->pri
, rfc3164_current_time(), hostname
, ctl
->tag
, pid
);
560 static inline struct list_head
*get_user_structured_data(struct logger_ctl
*ctl
)
562 return &ctl
->user_sds
;
565 static inline struct list_head
*get_reserved_structured_data(struct logger_ctl
*ctl
)
567 return &ctl
->reserved_sds
;
570 static int has_structured_data_id(struct list_head
*ls
, const char *id
)
574 if (!ls
|| list_empty(ls
))
577 list_for_each(p
, ls
) {
578 struct structured_data
*sd
= list_entry(p
, struct structured_data
, sds
);
579 if (sd
->id
&& strcmp(sd
->id
, id
) == 0)
586 static void add_structured_data_id(struct list_head
*ls
, const char *id
)
588 struct structured_data
*sd
;
592 if (has_structured_data_id(ls
, id
))
593 errx(EXIT_FAILURE
, _("structured data ID '%s' is not unique"), id
);
595 sd
= xcalloc(1, sizeof(*sd
));
596 INIT_LIST_HEAD(&sd
->sds
);
597 sd
->id
= xstrdup(id
);
599 list_add_tail(&sd
->sds
, ls
);
602 static void add_structured_data_param(struct list_head
*ls
, const char *param
)
604 struct structured_data
*sd
;
607 errx(EXIT_FAILURE
, _("--sd-id was not specified for --sd-param %s"), param
);
611 sd
= list_last_entry(ls
, struct structured_data
, sds
);
613 if (strv_extend(&sd
->params
, param
))
617 static void __attribute__ ((__format__ (__printf__
, 2, 3)))
618 add_structured_data_paramf(struct list_head
*ls
, const char *fmt
, ...)
620 struct structured_data
*sd
;
624 assert(!list_empty(ls
));
627 sd
= list_last_entry(ls
, struct structured_data
, sds
);
629 x
= strv_extendv(&sd
->params
, fmt
, ap
);
636 static char *strdup_structured_data(struct structured_data
*sd
)
640 if (strv_isempty(sd
->params
))
643 xasprintf(&res
, "[%s %s]", sd
->id
,
644 (tmp
= strv_join(sd
->params
, " ")));
649 static char *strdup_structured_data_list(struct list_head
*ls
)
654 list_for_each(p
, ls
) {
655 struct structured_data
*sd
= list_entry(p
, struct structured_data
, sds
);
656 char *one
= strdup_structured_data(sd
);
661 res
= strconcat(tmp
, one
);
669 static char *get_structured_data_string(struct logger_ctl
*ctl
)
671 char *sys
= NULL
, *usr
= NULL
, *res
;
673 if (!list_empty(&ctl
->reserved_sds
))
674 sys
= strdup_structured_data_list(&ctl
->reserved_sds
);
675 if (!list_empty(&ctl
->user_sds
))
676 usr
= strdup_structured_data_list(&ctl
->user_sds
);
679 res
= strconcat(sys
, usr
);
683 res
= sys
? sys
: usr
;
688 static int valid_structured_data_param(const char *str
)
691 char *eq
= strchr(str
, '='),
692 *qm1
= strchr(str
, '"'),
693 *qm2
= qm1
? ul_strchr_escaped(qm1
+ 1, '"') : NULL
;
695 /* something is missing */
696 if (!eq
|| !qm1
|| !qm2
)
699 /* ']' need to be escaped */
700 for (s
= qm1
+ 1; s
&& *s
; ) {
701 char *p
= strchr(s
, ']');
704 if (p
> qm2
|| p
== ul_strchr_escaped(s
, ']'))
709 /* '\' is allowed only before '[]"\' chars */
710 for (s
= qm1
+ 1; s
&& *s
; ) {
711 char *p
= strchr(s
, '\\');
714 if (!strchr("[]\"\\", *(p
+ 1)))
722 return eq
> str
&& eq
< qm1
&& eq
+ 1 == qm1
&& qm1
< qm2
&& *(qm2
+ 1) == '\0';
726 * name@<private enterprise number>, e.g., "ourSDID@32473"
728 static int valid_structured_data_id(const char *str
)
730 char *at
= strchr(str
, '@');
733 /* standardized IDs without @<digits> */
734 if (!at
&& (strcmp(str
, "timeQuality") == 0 ||
735 strcmp(str
, "origin") == 0 ||
736 strcmp(str
, "meta") == 0))
739 if (!at
|| at
== str
|| !*(at
+ 1))
742 /* <digits> or <digits>.<digits>[...] */
743 for (p
= at
+ 1; p
&& *p
; p
++) {
746 if (isdigit_strend(p
, &end
))
747 break; /* only digits in the string */
749 if (end
== NULL
|| end
== p
||
750 *end
!= '.' || *(end
+ 1) == '\0')
755 /* check for forbidden chars in the <name> */
756 for (p
= str
; p
< at
; p
++) {
757 if (*p
== '[' || *p
== '=' || *p
== '"' || *p
== '@')
759 if (isblank((unsigned char) *p
) || iscntrl((unsigned char) *p
))
766 /* Some field mappings may be controversial, thus I give the reason
767 * why this specific mapping was used:
769 * Some may argue that "logger" is a better fit, but we think
770 * this is better inline of what other implementations do. In
771 * rsyslog, for example, the TAG value is populated from APP-NAME.
773 * This is a relatively straightforward interpretation from
774 * RFC5424, sect. 6.2.6.
775 * MSGID <-- msgid (from --msgid)
776 * One may argue that the string "logger" would be better suited
777 * here so that a receiver can identify the sender process.
778 * However, this does not sound like a good match to RFC5424,
780 * Note that appendix A.1 of RFC5424 does not provide clear guidance
781 * of how these fields should be used. This is the case because the
782 * IETF working group couldn't arrive at a clear agreement when we
783 * specified RFC5424. The rest of the field mappings should be
784 * pretty clear from RFC5424. -- Rainer Gerhards, 2015-03-10
786 static void syslog_rfc5424_header(struct logger_ctl
*const ctl
)
791 char const *app_name
= ctl
->tag
;
793 char *const msgid
= xstrdup(ctl
->msgid
? ctl
->msgid
: NILVALUE
);
794 char *structured
= NULL
;
795 struct list_head
*sd
;
797 if (ctl
->rfc5424_time
) {
801 ret
= logger_gettimeofday(&tv
, NULL
);
803 err(EXIT_FAILURE
, _("gettimeofday() failed"));
804 if (localtime_r(&tv
.tv_sec
, &tm
) != NULL
) {
806 const size_t i
= strftime(fmt
, sizeof(fmt
),
807 "%Y-%m-%dT%H:%M:%S.%%06u%z ", &tm
);
808 /* patch TZ info to comply with RFC3339 (we left SP at end) */
809 fmt
[i
- 1] = fmt
[i
- 2];
810 fmt
[i
- 2] = fmt
[i
- 3];
812 xasprintf(&time
, fmt
, tv
.tv_usec
);
814 err(EXIT_FAILURE
, _("localtime() failed"));
816 time
= xstrdup(NILVALUE
);
818 if (ctl
->rfc5424_host
) {
819 if (!(hostname
= logger_xgethostname()))
820 hostname
= xstrdup(NILVALUE
);
821 /* Arbitrary looking 'if (var < strlen()) checks originate from
822 * RFC 5424 - 6 Syslog Message Format definition. */
823 if (255 < strlen(hostname
))
824 errx(EXIT_FAILURE
, _("hostname '%s' is too long"),
827 hostname
= xstrdup(NILVALUE
);
829 if (48 < strlen(ctl
->tag
))
830 errx(EXIT_FAILURE
, _("tag '%s' is too long"), ctl
->tag
);
833 xasprintf(&procid
, "%d", ctl
->pid
);
835 procid
= xstrdup(NILVALUE
);
837 sd
= get_reserved_structured_data(ctl
);
839 /* time quality structured data (maybe overwritten by --sd-id timeQuality) */
840 if (ctl
->rfc5424_tq
&& !has_structured_data_id(sd
, "timeQuality")) {
842 add_structured_data_id(sd
, "timeQuality");
843 add_structured_data_param(sd
, "tzKnown=\"1\"");
845 #ifdef HAVE_NTP_GETTIME
846 struct ntptimeval ntptv
;
848 if (ntp_gettime(&ntptv
) == TIME_OK
) {
849 add_structured_data_param(sd
, "isSynced=\"1\"");
850 add_structured_data_paramf(sd
, "syncAccuracy=\"%ld\"", ntptv
.maxerror
);
853 add_structured_data_paramf(sd
, "isSynced=\"0\"");
856 /* convert all structured data to string */
857 structured
= get_structured_data_string(ctl
);
859 structured
= xstrdup(NILVALUE
);
861 xasprintf(&ctl
->hdr
, "<%d>1 %s %s %s %s %s %s ",
872 /* app_name points to ctl->tag, do NOT free! */
878 static void parse_rfc5424_flags(struct logger_ctl
*ctl
, char *s
)
883 while ((tok
= strtok(in
, ","))) {
885 if (!strcmp(tok
, "notime")) {
886 ctl
->rfc5424_time
= 0;
888 } else if (!strcmp(tok
, "notq"))
890 else if (!strcmp(tok
, "nohost"))
891 ctl
->rfc5424_host
= 0;
893 warnx(_("ignoring unknown option argument: %s"), tok
);
897 static int parse_unix_socket_errors_flags(char *s
)
899 if (!strcmp(s
, "off"))
900 return AF_UNIX_ERRORS_OFF
;
901 if (!strcmp(s
, "on"))
902 return AF_UNIX_ERRORS_ON
;
903 if (!strcmp(s
, "auto"))
904 return AF_UNIX_ERRORS_AUTO
;
905 warnx(_("invalid argument: %s: using automatic errors"), s
);
906 return AF_UNIX_ERRORS_AUTO
;
909 static void syslog_local_header(struct logger_ctl
*const ctl
)
914 snprintf(pid
, sizeof(pid
), "[%d]", ctl
->pid
);
918 xasprintf(&ctl
->hdr
, "<%d>%s %s%s: ", ctl
->pri
, rfc3164_current_time(),
922 static void generate_syslog_header(struct logger_ctl
*const ctl
)
929 /* just open, nothing else */
930 static void __logger_open(struct logger_ctl
*ctl
)
933 ctl
->fd
= inet_socket(ctl
->server
, ctl
->port
, &ctl
->socket_type
);
935 if (!ctl
->unix_socket
)
936 ctl
->unix_socket
= _PATH_DEVLOG
;
938 ctl
->fd
= unix_socket(ctl
, ctl
->unix_socket
, &ctl
->socket_type
);
942 /* open and initialize relevant @ctl tuff */
943 static void logger_open(struct logger_ctl
*ctl
)
948 ctl
->syslogfp
= ctl
->server
? syslog_rfc5424_header
:
951 ctl
->tag
= ctl
->login
= xgetlogin();
953 ctl
->tag
= "<someone>";
956 /* re-open; usually after failed connection */
957 static void logger_reopen(struct logger_ctl
*ctl
)
966 static void logger_command_line(struct logger_ctl
*ctl
, char **argv
)
968 /* note: we never re-generate the syslog header here, even if we
969 * generate multiple messages. If so, we think it is the right thing
970 * to do to report them with the same timestamp, as the user actually
971 * intended to send a single message.
973 char *const buf
= xmalloc(ctl
->max_message_size
+ 1);
975 const char *endp
= buf
+ ctl
->max_message_size
- 1;
980 if (endp
< p
+ len
&& p
!= buf
) {
981 write_output(ctl
, buf
);
984 if (ctl
->max_message_size
< len
) {
985 (*argv
)[ctl
->max_message_size
] = '\0'; /* truncate */
986 write_output(ctl
, *argv
++);
991 memmove(p
, *argv
++, len
);
995 write_output(ctl
, buf
);
999 static void logger_stdin(struct logger_ctl
*ctl
)
1001 /* note: we re-generate the syslog header for each log message to
1002 * update header timestamps and to reflect possible priority changes.
1004 int default_priority
= ctl
->pri
;
1005 char *buf
= xmalloc(ctl
->max_message_size
+ 2 + 2);
1013 if (ctl
->prio_prefix
&& c
== '<') {
1016 while (isdigit(c
= getchar()) && pri
<= 191) {
1018 pri
= pri
* 10 + c
- '0';
1020 if (c
!= EOF
&& c
!= '\n')
1022 if (c
== '>' && 0 <= pri
&& pri
<= 191) {
1023 /* valid RFC PRI values */
1025 if ((pri
& LOG_FACMASK
) == 0)
1026 pri
|= (default_priority
& LOG_FACMASK
);
1029 ctl
->pri
= default_priority
;
1031 if (c
!= EOF
&& c
!= '\n')
1035 while (c
!= EOF
&& c
!= '\n' && i
< ctl
->max_message_size
) {
1041 if (i
> 0 || !ctl
->skip_empty_lines
) {
1042 generate_syslog_header(ctl
);
1043 write_output(ctl
, buf
);
1046 if (c
== '\n') /* discard line terminator */
1053 static void logger_close(const struct logger_ctl
*ctl
)
1055 if (ctl
->fd
!= -1 && close(ctl
->fd
) != 0)
1056 err(EXIT_FAILURE
, _("close failed"));
1061 static void __attribute__((__noreturn__
)) usage(void)
1064 fputs(USAGE_HEADER
, out
);
1065 fprintf(out
, _(" %s [options] [<message>]\n"), program_invocation_short_name
);
1067 fputs(USAGE_SEPARATOR
, out
);
1068 fputs(_("Enter messages into the system log.\n"), out
);
1070 fputs(USAGE_OPTIONS
, out
);
1071 fputs(_(" -i log the logger command's PID\n"), out
);
1072 fputs(_(" --id[=<id>] log the given <id>, or otherwise the PID\n"), out
);
1073 fputs(_(" -f, --file <file> log the contents of this file\n"), out
);
1074 fputs(_(" -e, --skip-empty do not log empty lines when processing files\n"), out
);
1075 fputs(_(" --no-act do everything except the write the log\n"), out
);
1076 fputs(_(" -p, --priority <prio> mark given message with this priority\n"), out
);
1077 fputs(_(" --octet-count use rfc6587 octet counting\n"), out
);
1078 fputs(_(" --prio-prefix look for a prefix on every line read from stdin\n"), out
);
1079 fputs(_(" -s, --stderr output message to standard error as well\n"), out
);
1080 fputs(_(" -S, --size <size> maximum size for a single message\n"), out
);
1081 fputs(_(" -t, --tag <tag> mark every line with this tag\n"), out
);
1082 fputs(_(" -n, --server <name> write to this remote syslog server\n"), out
);
1083 fputs(_(" -P, --port <port> use this port for UDP or TCP connection\n"), out
);
1084 fputs(_(" -T, --tcp use TCP only\n"), out
);
1085 fputs(_(" -d, --udp use UDP only\n"), out
);
1086 fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out
);
1087 fputs(_(" --rfc5424[=<snip>] use the syslog protocol (the default for remote);\n"
1088 " <snip> can be notime, or notq, and/or nohost\n"), out
);
1089 fputs(_(" --sd-id <id> rfc5424 structured data ID\n"), out
);
1090 fputs(_(" --sd-param <data> rfc5424 structured data name=value\n"), out
);
1091 fputs(_(" --msgid <msgid> set rfc5424 message id field\n"), out
);
1092 fputs(_(" -u, --socket <socket> write to this Unix socket\n"), out
);
1093 fputs(_(" --socket-errors[=<on|off|auto>]\n"
1094 " print connection errors when using Unix sockets\n"), out
);
1095 #ifdef HAVE_LIBSYSTEMD
1096 fputs(_(" --journald[=<file>] write journald entry\n"), out
);
1099 fputs(USAGE_SEPARATOR
, out
);
1100 fprintf(out
, USAGE_HELP_OPTIONS(26));
1101 fprintf(out
, USAGE_MAN_TAIL("logger(1)"));
1107 * logger -- read and log utility
1109 * Reads from an input and arranges to write the result on the system
1112 int main(int argc
, char **argv
)
1114 struct logger_ctl ctl
= {
1117 .pri
= LOG_USER
| LOG_NOTICE
,
1120 .unix_socket
= NULL
,
1121 .unix_socket_errors
= 0,
1126 .socket_type
= ALL_TYPES
,
1127 .max_message_size
= 1024,
1131 .skip_empty_lines
= 0
1134 int stdout_reopened
= 0;
1135 int unix_socket_errors_mode
= AF_UNIX_ERRORS_AUTO
;
1136 #ifdef HAVE_LIBSYSTEMD
1139 static const struct option longopts
[] = {
1140 { "id", optional_argument
, 0, OPT_ID
},
1141 { "stderr", no_argument
, 0, 's' },
1142 { "file", required_argument
, 0, 'f' },
1143 { "no-act", no_argument
, 0, OPT_NOACT
, },
1144 { "priority", required_argument
, 0, 'p' },
1145 { "tag", required_argument
, 0, 't' },
1146 { "socket", required_argument
, 0, 'u' },
1147 { "socket-errors", required_argument
, 0, OPT_SOCKET_ERRORS
},
1148 { "udp", no_argument
, 0, 'd' },
1149 { "tcp", no_argument
, 0, 'T' },
1150 { "server", required_argument
, 0, 'n' },
1151 { "port", required_argument
, 0, 'P' },
1152 { "version", no_argument
, 0, 'V' },
1153 { "help", no_argument
, 0, 'h' },
1154 { "octet-count", no_argument
, 0, OPT_OCTET_COUNT
},
1155 { "prio-prefix", no_argument
, 0, OPT_PRIO_PREFIX
},
1156 { "rfc3164", no_argument
, 0, OPT_RFC3164
},
1157 { "rfc5424", optional_argument
, 0, OPT_RFC5424
},
1158 { "size", required_argument
, 0, 'S' },
1159 { "msgid", required_argument
, 0, OPT_MSGID
},
1160 { "skip-empty", no_argument
, 0, 'e' },
1161 { "sd-id", required_argument
, 0, OPT_STRUCTURED_DATA_ID
},
1162 { "sd-param", required_argument
, 0, OPT_STRUCTURED_DATA_PARAM
},
1163 #ifdef HAVE_LIBSYSTEMD
1164 { "journald", optional_argument
, 0, OPT_JOURNALD
},
1169 setlocale(LC_ALL
, "");
1170 bindtextdomain(PACKAGE
, LOCALEDIR
);
1171 textdomain(PACKAGE
);
1172 close_stdout_atexit();
1174 INIT_LIST_HEAD(&ctl
.user_sds
);
1175 INIT_LIST_HEAD(&ctl
.reserved_sds
);
1177 while ((ch
= getopt_long(argc
, argv
, "ef:ip:S:st:u:dTn:P:Vh",
1178 longopts
, NULL
)) != -1) {
1180 case 'f': /* file to log */
1181 if (freopen(optarg
, "r", stdin
) == NULL
)
1182 err(EXIT_FAILURE
, _("file %s"), optarg
);
1183 stdout_reopened
= 1;
1186 ctl
.skip_empty_lines
= 1;
1188 case 'i': /* log process id also */
1189 ctl
.pid
= logger_getpid();
1193 const char *p
= optarg
;
1197 ctl
.pid
= strtoul_or_err(optarg
, _("failed to parse id"));
1199 ctl
.pid
= logger_getpid();
1201 case 'p': /* priority */
1202 ctl
.pri
= pencode(optarg
);
1204 case 's': /* log to standard error */
1205 ctl
.stderr_printout
= 1;
1210 case 'u': /* unix socket */
1211 ctl
.unix_socket
= optarg
;
1213 case 'S': /* max message size */
1214 ctl
.max_message_size
= strtosize_or_err(optarg
,
1215 _("failed to parse message size"));
1218 ctl
.socket_type
= TYPE_UDP
;
1221 ctl
.socket_type
= TYPE_TCP
;
1224 ctl
.server
= optarg
;
1229 case OPT_OCTET_COUNT
:
1230 ctl
.octet_count
= 1;
1232 case OPT_PRIO_PREFIX
:
1233 ctl
.prio_prefix
= 1;
1236 ctl
.syslogfp
= syslog_rfc3164_header
;
1239 ctl
.syslogfp
= syslog_rfc5424_header
;
1241 parse_rfc5424_flags(&ctl
, optarg
);
1244 if (strchr(optarg
, ' '))
1245 errx(EXIT_FAILURE
, _("--msgid cannot contain space"));
1248 #ifdef HAVE_LIBSYSTEMD
1251 jfd
= fopen(optarg
, "r");
1253 err(EXIT_FAILURE
, _("cannot open %s"),
1259 case OPT_SOCKET_ERRORS
:
1260 unix_socket_errors_mode
= parse_unix_socket_errors_flags(optarg
);
1265 case OPT_STRUCTURED_DATA_ID
:
1266 if (!valid_structured_data_id(optarg
))
1267 errx(EXIT_FAILURE
, _("invalid structured data ID: '%s'"), optarg
);
1268 add_structured_data_id(get_user_structured_data(&ctl
), optarg
);
1270 case OPT_STRUCTURED_DATA_PARAM
:
1271 if (!valid_structured_data_param(optarg
))
1272 errx(EXIT_FAILURE
, _("invalid structured data parameter: '%s'"), optarg
);
1273 add_structured_data_param(get_user_structured_data(&ctl
), optarg
);
1277 print_version(EXIT_SUCCESS
);
1281 errtryhelp(EXIT_FAILURE
);
1286 if (stdout_reopened
&& argc
)
1287 warnx(_("--file <file> and <message> are mutually exclusive, message is ignored"));
1288 #ifdef HAVE_LIBSYSTEMD
1290 int ret
= journald_entry(&ctl
, jfd
);
1294 errx(EXIT_FAILURE
, _("journald entry could not be written"));
1295 return EXIT_SUCCESS
;
1299 /* user overwrites built-in SD-ELEMENT */
1300 if (has_structured_data_id(get_user_structured_data(&ctl
), "timeQuality"))
1303 switch (unix_socket_errors_mode
) {
1304 case AF_UNIX_ERRORS_OFF
:
1305 ctl
.unix_socket_errors
= 0;
1307 case AF_UNIX_ERRORS_ON
:
1308 ctl
.unix_socket_errors
= 1;
1310 case AF_UNIX_ERRORS_AUTO
:
1311 ctl
.unix_socket_errors
= ctl
.noact
|| ctl
.stderr_printout
;
1312 #ifdef HAVE_LIBSYSTEMD
1313 ctl
.unix_socket_errors
|= !!sd_booted();
1321 generate_syslog_header(&ctl
);
1322 logger_command_line(&ctl
, argv
);
1324 /* Note. --file <arg> reopens stdin making the below
1325 * function to be used for file inputs. */
1329 return EXIT_SUCCESS
;