#include <netdb.h>
#include <getopt.h>
#include <pwd.h>
-#include <sys/types.h>
#include <signal.h>
#include <sys/uio.h>
#include <syslog.h>
#ifdef HAVE_LIBSYSTEMD
+# define SD_JOURNAL_SUPPRESS_LOCATION
# include <systemd/sd-daemon.h>
# include <systemd/sd-journal.h>
#endif
int pri;
pid_t pid; /* zero when unwanted */
char *hdr; /* the syslog header (based on protocol) */
- char *tag;
+ char const *tag;
char *msgid;
char *unix_socket; /* -u <path> or default to _PATH_DEVLOG */
char *server;
unsigned int
unix_socket_errors:1, /* whether to report or not errors */
noact:1, /* do not write to sockets */
- prio_prefix:1, /* read priority from intput */
+ prio_prefix:1, /* read priority from input */
stderr_printout:1, /* output message to stderr */
rfc5424_time:1, /* include time stamp */
rfc5424_tq:1, /* include time quality markup */
octet_count:1; /* use RFC6587 octet counting */
};
+#define is_connected(_ctl) ((_ctl)->fd >= 0)
+static void logger_reopen(struct logger_ctl *ctl);
+
/*
* For tests we want to be able to control datetime outputs
*/
static int unix_socket(struct logger_ctl *ctl, const char *path, int *socket_type)
{
- int fd, i, type = -1;
+ int fd = -1, i, type = -1;
static struct sockaddr_un s_addr; /* AF_UNIX address of local logger */
if (strlen(path) >= sizeof(s_addr.sun_path))
if (ctl->unix_socket_errors)
err(EXIT_FAILURE, _("socket %s"), path);
- /* openlog(3) compatibility, socket errors are
- * not reported, but ignored silently */
- ctl->noact = 1;
+ /* write_output() will try to reconnect */
return -1;
}
struct iovec *iovec;
char *buf = NULL;
ssize_t sz;
- int n, lines, vectors = 8, ret = 0;
+ int n, lines = 0, vectors = 8, ret = 0, msgline = -1;
size_t dummy = 0;
iovec = xmalloc(vectors * sizeof(struct iovec));
- for (lines = 0; /* nothing */ ; lines++) {
+ while (1) {
buf = NULL;
sz = getline(&buf, &dummy, fp);
- if (sz == -1)
+ if (sz == -1 ||
+ (sz = rtrim_whitespace((unsigned char *) buf)) == 0) {
+ free(buf);
break;
- if (0 < sz && buf[sz - 1] == '\n') {
- sz--;
- buf[sz] = '\0';
}
+
+ if (strncmp(buf, "MESSAGE=", 8) == 0) {
+ if (msgline == -1)
+ msgline = lines; /* remember the first message */
+ else {
+ char *p = xrealloc(iovec[msgline].iov_base,
+ iovec[msgline].iov_len + sz - 8 + 2);
+
+ iovec[msgline].iov_base = p;
+ p += iovec[msgline].iov_len;
+ *p++ = '\n';
+ memcpy(p, buf + 8, sz - 8);
+ free(buf);
+
+ iovec[msgline].iov_len += sz - 8 + 1;
+ continue;
+ }
+ }
+
if (lines == vectors) {
vectors *= 2;
if (IOV_MAX < vectors)
}
iovec[lines].iov_base = buf;
iovec[lines].iov_len = sz;
+ ++lines;
}
if (!ctl->noact)
}
#endif
-static char *xgetlogin(void)
+static char const *xgetlogin(void)
{
- char *cp;
+ char const *cp;
struct passwd *pw;
if (!(cp = getlogin()) || !*cp)
* of a leading 0). The function uses a static buffer which is
* overwritten on the next call (just like ctime() does).
*/
-static const char *rfc3164_current_time(void)
+static char const *rfc3164_current_time(void)
{
static char time[32];
struct timeval tv;
struct tm *tm;
- static char *monthnames[] = {
+ static char const * const monthnames[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
};
* it is too much for the logger utility. If octet-counting is
* selected, we use that.
*/
-static void write_output(const struct logger_ctl *ctl, const char *const msg)
+static void write_output(struct logger_ctl *ctl, const char *const msg)
{
struct iovec iov[4];
int iovlen = 0;
char *octet = NULL;
+ /* initial connect failed? */
+ if (!ctl->noact && !is_connected(ctl))
+ logger_reopen(ctl);
+
/* 1) octen count */
if (ctl->octet_count) {
size_t len = xasprintf(&octet, "%zu ", strlen(ctl->hdr) + strlen(msg));
/* 3) message */
iovec_add_string(iov, iovlen, msg, 0);
- if (!ctl->noact) {
+ if (!ctl->noact && is_connected(ctl)) {
struct msghdr message = { 0 };
#ifdef SCM_CREDENTIALS
struct cmsghdr *cmhp;
cred->pid = ctl->pid;
}
#endif
-
- if (sendmsg(ctl->fd, &message, 0) < 0)
- warn(_("send message failed"));
+ /* Note that logger(1) maybe executed for long time (as pipe
+ * reader) and connection endpoint (syslogd) may be restarted.
+ *
+ * The libc syslog() function reconnects on failed send().
+ * Let's do the same to be robust. [kzak -- Oct 2017]
+ *
+ * MSG_NOSIGNAL is POSIX.1-2008 compatible, but it for example
+ * not supported by apple-darwin15.6.0.
+ */
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+ if (sendmsg(ctl->fd, &message, MSG_NOSIGNAL) < 0) {
+ logger_reopen(ctl);
+ if (sendmsg(ctl->fd, &message, MSG_NOSIGNAL) < 0)
+ warn(_("send message failed"));
+ }
}
if (ctl->stderr_printout) {
char pid[30], *hostname;
*pid = '\0';
- if (ctl->fd < 0)
- return;
if (ctl->pid)
snprintf(pid, sizeof(pid), "[%d]", ctl->pid);
struct structured_data *sd;
if (list_empty(ls))
- errx(EXIT_FAILURE, _("--sd-id no specified for --sd-param %s"), param);
+ errx(EXIT_FAILURE, _("--sd-id was not specified for --sd-param %s"), param);
assert(param);
if (!at || at == str || !*(at + 1))
return 0;
- if (!isdigit_string(at + 1))
- return 0;
+
+ /* <digits> or <digits>.<digits>[...] */
+ for (p = at + 1; p && *p; p++) {
+ const char *end;
+
+ if (isdigit_strend(p, &end))
+ break; /* only digits in the string */
+
+ if (end == NULL || end == p ||
+ *end != '.' || *(end + 1) == '\0')
+ return 0;
+ p = end;
+ }
/* check for forbidden chars in the <name> */
for (p = str; p < at; p++) {
if (*p == '[' || *p == '=' || *p == '"' || *p == '@')
return 0;
- if (isblank((unsigned int) *p) || iscntrl((unsigned int) *p))
+ if (isblank((unsigned char) *p) || iscntrl((unsigned char) *p))
return 0;
}
return 1;
{
char *time;
char *hostname;
- char *const app_name = ctl->tag;
+ char const *app_name = ctl->tag;
char *procid;
char *const msgid = xstrdup(ctl->msgid ? ctl->msgid : NILVALUE);
char *structured = NULL;
struct list_head *sd;
- if (ctl->fd < 0)
- return;
-
if (ctl->rfc5424_time) {
struct timeval tv;
struct tm *tm;
sd = get_reserved_structured_data(ctl);
- /* time quality structured data (maybe overwriten by --sd-id timeQuality) */
+ /* time quality structured data (maybe overwritten by --sd-id timeQuality) */
if (ctl->rfc5424_tq && !has_structured_data_id(sd, "timeQuality")) {
add_structured_data_id(sd, "timeQuality");
free(structured);
}
-static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg)
+static void parse_rfc5424_flags(struct logger_ctl *ctl, char *s)
{
char *in, *tok;
- in = optarg;
+ in = s;
while ((tok = strtok(in, ","))) {
in = NULL;
if (!strcmp(tok, "notime")) {
}
}
-static int parse_unix_socket_errors_flags(char *optarg)
+static int parse_unix_socket_errors_flags(char *s)
{
- if (!strcmp(optarg, "off"))
+ if (!strcmp(s, "off"))
return AF_UNIX_ERRORS_OFF;
- if (!strcmp(optarg, "on"))
+ if (!strcmp(s, "on"))
return AF_UNIX_ERRORS_ON;
- if (!strcmp(optarg, "auto"))
+ if (!strcmp(s, "auto"))
return AF_UNIX_ERRORS_AUTO;
- warnx(_("invalid argument: %s: using automatic errors"), optarg);
+ warnx(_("invalid argument: %s: using automatic errors"), s);
return AF_UNIX_ERRORS_AUTO;
}
static void generate_syslog_header(struct logger_ctl *const ctl)
{
free(ctl->hdr);
+ ctl->hdr = NULL;
ctl->syslogfp(ctl);
}
-static void logger_open(struct logger_ctl *ctl)
+/* just open, nothing else */
+static void __logger_open(struct logger_ctl *ctl)
{
if (ctl->server) {
ctl->fd = inet_socket(ctl->server, ctl->port, &ctl->socket_type);
- if (!ctl->syslogfp)
- ctl->syslogfp = syslog_rfc5424_header;
} else {
if (!ctl->unix_socket)
ctl->unix_socket = _PATH_DEVLOG;
ctl->fd = unix_socket(ctl, ctl->unix_socket, &ctl->socket_type);
- if (!ctl->syslogfp)
- ctl->syslogfp = syslog_local_header;
}
+}
+
+/* open and initialize relevant @ctl tuff */
+static void logger_open(struct logger_ctl *ctl)
+{
+ __logger_open(ctl);
+
+ if (!ctl->syslogfp)
+ ctl->syslogfp = ctl->server ? syslog_rfc5424_header :
+ syslog_local_header;
if (!ctl->tag)
ctl->tag = xgetlogin();
+
generate_syslog_header(ctl);
}
-static void logger_command_line(const struct logger_ctl *ctl, char **argv)
+/* re-open; usually after failed connection */
+static void logger_reopen(struct logger_ctl *ctl)
+{
+ if (ctl->fd != -1)
+ close(ctl->fd);
+ ctl->fd = -1;
+
+ __logger_open(ctl);
+}
+
+static void logger_command_line(struct logger_ctl *ctl, char **argv)
{
/* note: we never re-generate the syslog header here, even if we
* generate multiple messages. If so, we think it is the right thing
static void logger_stdin(struct logger_ctl *ctl)
{
- /* note: we re-generate the the syslog header for each log message to
+ /* note: we re-generate the syslog header for each log message to
* update header timestamps and to reflect possible priority changes.
* The initial header is generated by logger_open().
*/
c = getchar();
while (c != EOF) {
i = 0;
- if (ctl->prio_prefix) {
- if (c == '<') {
- pri = 0;
+ if (ctl->prio_prefix && c == '<') {
+ pri = 0;
+ buf[i++] = c;
+ while (isdigit(c = getchar()) && pri <= 191) {
+ buf[i++] = c;
+ pri = pri * 10 + c - '0';
+ }
+ if (c != EOF && c != '\n')
buf[i++] = c;
- while (isdigit(c = getchar()) && pri <= 191) {
- buf[i++] = c;
- pri = pri * 10 + c - '0';
- }
- if (c != EOF && c != '\n')
- buf[i++] = c;
- if (c == '>' && 0 <= pri && pri <= 191) { /* valid RFC PRI values */
- i = 0;
- if (pri < 8)
- pri |= 8; /* kern facility is forbidden */
- ctl->pri = pri;
- } else
- ctl->pri = default_priority;
-
- if (ctl->pri != last_pri) {
- has_header = 0;
- max_usrmsg_size = ctl->max_message_size - strlen(ctl->hdr);
- last_pri = ctl->pri;
- }
- if (c != EOF && c != '\n')
- c = getchar();
+ if (c == '>' && 0 <= pri && pri <= 191) {
+ /* valid RFC PRI values */
+ i = 0;
+ if (pri < 8) /* kern facility is forbidden */
+ pri |= 8;
+ ctl->pri = pri;
+ } else
+ ctl->pri = default_priority;
+
+ if (ctl->pri != last_pri) {
+ has_header = 0;
+ max_usrmsg_size =
+ ctl->max_message_size - strlen(ctl->hdr);
+ last_pri = ctl->pri;
}
+ if (c != EOF && c != '\n')
+ c = getchar();
}
while (c != EOF && c != '\n' && i < max_usrmsg_size) {
if (c == '\n') /* discard line terminator */
c = getchar();
}
+
+ free(buf);
}
static void logger_close(const struct logger_ctl *ctl)
free(ctl->hdr);
}
-static void __attribute__ ((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options] [<message>]\n"), program_invocation_short_name);
fputs(_(" -S, --size <size> maximum size for a single message\n"), out);
fputs(_(" -t, --tag <tag> mark every line with this tag\n"), out);
fputs(_(" -n, --server <name> write to this remote syslog server\n"), out);
- fputs(_(" -P, --port <number> use this UDP port\n"), out);
+ fputs(_(" -P, --port <port> use this port for UDP or TCP connection\n"), out);
fputs(_(" -T, --tcp use TCP only\n"), out);
fputs(_(" -d, --udp use UDP only\n"), out);
fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out);
#endif
fputs(USAGE_SEPARATOR, out);
- fputs(USAGE_HELP, out);
- fputs(USAGE_VERSION, out);
- fprintf(out, USAGE_MAN_TAIL("logger(1)"));
+ printf(USAGE_HELP_OPTIONS(26));
+ printf(USAGE_MAN_TAIL("logger(1)"));
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ exit(EXIT_SUCCESS);
}
/*
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
INIT_LIST_HEAD(&ctl.user_sds);
INIT_LIST_HEAD(&ctl.reserved_sds);
case 'P':
ctl.port = optarg;
break;
- case 'V':
- printf(UTIL_LINUX_VERSION);
- exit(EXIT_SUCCESS);
- case 'h':
- usage(stdout);
case OPT_OCTET_COUNT:
ctl.octet_count = 1;
break;
errx(EXIT_FAILURE, _("invalid structured data parameter: '%s'"), optarg);
add_structured_data_param(get_user_structured_data(&ctl), optarg);
break;
- case '?':
+
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
argc -= optind;
}
#endif
- /* user overwrites build-in SD-ELEMENT */
+ /* user overwrites built-in SD-ELEMENT */
if (has_structured_data_id(get_user_structured_data(&ctl), "timeQuality"))
ctl.rfc5424_tq = 0;