]>
Commit | Line | Data |
---|---|---|
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 |
75 | enum { |
76 | TYPE_UDP = (1 << 1), | |
77 | TYPE_TCP = (1 << 2), | |
78 | ALL_TYPES = TYPE_UDP | TYPE_TCP | |
79 | }; | |
80 | ||
d77dc29e SK |
81 | enum { |
82 | AF_UNIX_ERRORS_OFF = 0, | |
83 | AF_UNIX_ERRORS_ON, | |
84 | AF_UNIX_ERRORS_AUTO | |
85 | }; | |
86 | ||
98920f80 | 87 | enum { |
4b670c01 | 88 | OPT_PRIO_PREFIX = CHAR_MAX + 1, |
4de2e8a0 SK |
89 | OPT_JOURNALD, |
90 | OPT_RFC3164, | |
d77dc29e | 91 | OPT_RFC5424, |
3f51c10b SK |
92 | OPT_SOCKET_ERRORS, |
93 | OPT_ID | |
98920f80 DJ |
94 | }; |
95 | ||
cfa77d26 SK |
96 | struct logger_ctl { |
97 | int fd; | |
cfa77d26 | 98 | int pri; |
59c6ac0b | 99 | pid_t pid; /* zero when unwanted */ |
cfa77d26 | 100 | char *tag; |
c68a1cb4 SK |
101 | char *unix_socket; |
102 | char *server; | |
103 | char *port; | |
104 | int socket_type; | |
46ee14df | 105 | void (*syslogfp)(const struct logger_ctl *ctl, const char *msg); |
4de2e8a0 | 106 | unsigned int |
d77dc29e SK |
107 | unix_socket_errors:1, /* whether to report or not errors */ |
108 | prio_prefix:1, /* read priority from intput */ | |
109 | stderr_printout:1, /* output message to stderr */ | |
110 | rfc5424_time:1, /* include time stamp */ | |
111 | rfc5424_tq:1, /* include time quality markup */ | |
112 | rfc5424_host:1; /* include hostname */ | |
cfa77d26 SK |
113 | }; |
114 | ||
d8b616c2 | 115 | static char *get_prio_prefix(char *msg, int *prio) |
98920f80 DJ |
116 | { |
117 | int p; | |
118 | char *end = NULL; | |
119 | int facility = *prio & LOG_FACMASK; | |
120 | ||
121 | errno = 0; | |
122 | p = strtoul(msg + 1, &end, 10); | |
123 | ||
124 | if (errno || !end || end == msg + 1 || end[0] != '>') | |
125 | return msg; | |
126 | ||
127 | if (p & LOG_FACMASK) | |
128 | facility = p & LOG_FACMASK; | |
129 | ||
130 | *prio = facility | (p & LOG_PRIMASK); | |
131 | return end + 1; | |
132 | } | |
133 | ||
46ee14df | 134 | static int decode(const char *name, CODE *codetab) |
82054b1d DR |
135 | { |
136 | register CODE *c; | |
137 | ||
4d7d1af6 SK |
138 | if (name == NULL || *name == '\0') |
139 | return -1; | |
140 | if (isdigit(*name)) { | |
141 | int num; | |
142 | char *end = NULL; | |
143 | ||
144 | num = strtol(name, &end, 10); | |
145 | if (errno || name == end || (end && *end)) | |
146 | return -1; | |
147 | for (c = codetab; c->c_name; c++) | |
148 | if (num == c->c_val) | |
149 | return num; | |
150 | return -1; | |
151 | } | |
82054b1d DR |
152 | for (c = codetab; c->c_name; c++) |
153 | if (!strcasecmp(name, c->c_name)) | |
154 | return (c->c_val); | |
155 | ||
156 | return -1; | |
157 | } | |
158 | ||
159 | static int pencode(char *s) | |
160 | { | |
2e0fd22d SK |
161 | int facility, level; |
162 | char *separator; | |
163 | ||
164 | separator = strchr(s, '.'); | |
165 | if (separator) { | |
166 | *separator = '\0'; | |
167 | facility = decode(s, facilitynames); | |
168 | if (facility < 0) | |
169 | errx(EXIT_FAILURE, _("unknown facility name: %s"), s); | |
170 | s = ++separator; | |
171 | } else | |
172 | facility = LOG_USER; | |
173 | level = decode(s, prioritynames); | |
174 | if (level < 0) | |
175 | errx(EXIT_FAILURE, _("unknown priority name: %s"), s); | |
176 | return ((level & LOG_PRIMASK) | (facility & LOG_FACMASK)); | |
82054b1d DR |
177 | } |
178 | ||
d77dc29e | 179 | static int unix_socket(struct logger_ctl *ctl, const char *path, const int socket_type) |
fe6999da | 180 | { |
68265d07 | 181 | int fd, i; |
fe6999da | 182 | static struct sockaddr_un s_addr; /* AF_UNIX address of local logger */ |
7eda085c | 183 | |
68265d07 SK |
184 | if (strlen(path) >= sizeof(s_addr.sun_path)) |
185 | errx(EXIT_FAILURE, _("openlog %s: pathname too long"), path); | |
7eda085c | 186 | |
fe6999da | 187 | s_addr.sun_family = AF_UNIX; |
68265d07 SK |
188 | strcpy(s_addr.sun_path, path); |
189 | ||
190 | for (i = 2; i; i--) { | |
191 | int st = -1; | |
49999d6a | 192 | |
68265d07 SK |
193 | if (i == 2 && socket_type & TYPE_UDP) |
194 | st = SOCK_DGRAM; | |
195 | if (i == 1 && socket_type & TYPE_TCP) | |
196 | st = SOCK_STREAM; | |
197 | if (st == -1 || (fd = socket(AF_UNIX, st, 0)) == -1) | |
198 | continue; | |
fe6999da SK |
199 | if (connect(fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) == -1) { |
200 | close(fd); | |
68265d07 | 201 | continue; |
fe6999da | 202 | } |
68265d07 | 203 | break; |
fe6999da | 204 | } |
7eda085c | 205 | |
d77dc29e SK |
206 | if (i == 0) { |
207 | if (ctl->unix_socket_errors) | |
208 | err(EXIT_FAILURE, _("socket %s"), path); | |
209 | else | |
210 | /* See --socket-errors manual page entry for | |
211 | * explanation of this strange exit. */ | |
212 | exit(EXIT_SUCCESS); | |
213 | } | |
fe6999da | 214 | return fd; |
7eda085c KZ |
215 | } |
216 | ||
195c3603 KZ |
217 | static int inet_socket(const char *servername, const char *port, |
218 | const int socket_type) | |
68265d07 SK |
219 | { |
220 | int fd, errcode, i; | |
24f4db69 | 221 | struct addrinfo hints, *res; |
68265d07 SK |
222 | const char *p = port; |
223 | ||
224 | for (i = 2; i; i--) { | |
225 | memset(&hints, 0, sizeof(hints)); | |
226 | if (i == 2 && socket_type & TYPE_UDP) { | |
227 | hints.ai_socktype = SOCK_DGRAM; | |
228 | if (port == NULL) | |
229 | p = "syslog"; | |
230 | } | |
231 | if (i == 1 && socket_type & TYPE_TCP) { | |
232 | hints.ai_socktype = SOCK_STREAM; | |
233 | if (port == NULL) | |
234 | p = "syslog-conn"; | |
235 | } | |
236 | if (hints.ai_socktype == 0) | |
237 | continue; | |
238 | hints.ai_family = AF_UNSPEC; | |
239 | errcode = getaddrinfo(servername, p, &hints, &res); | |
240 | if (errcode != 0) | |
4ce393f4 | 241 | errx(EXIT_FAILURE, _("failed to resolve name %s port %s: %s"), |
68265d07 SK |
242 | servername, p, gai_strerror(errcode)); |
243 | if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { | |
244 | freeaddrinfo(res); | |
245 | continue; | |
246 | } | |
247 | if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) { | |
248 | freeaddrinfo(res); | |
249 | close(fd); | |
250 | continue; | |
251 | } | |
24f4db69 | 252 | |
68265d07 SK |
253 | freeaddrinfo(res); |
254 | break; | |
255 | } | |
912d6b98 | 256 | |
68265d07 | 257 | if (i == 0) |
4ce393f4 | 258 | errx(EXIT_FAILURE, _("failed to connect to %s port %s"), servername, p); |
49999d6a | 259 | |
912d6b98 WJE |
260 | return fd; |
261 | } | |
68265d07 | 262 | |
ebff016a | 263 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
264 | static int journald_entry(FILE *fp) |
265 | { | |
266 | struct iovec *iovec; | |
267 | char *buf = NULL; | |
268 | ssize_t sz; | |
269 | int n, lines, vectors = 8, ret; | |
270 | size_t dummy = 0; | |
271 | ||
272 | iovec = xmalloc(vectors * sizeof(struct iovec)); | |
273 | for (lines = 0; /* nothing */ ; lines++) { | |
274 | buf = NULL; | |
275 | sz = getline(&buf, &dummy, fp); | |
276 | if (sz == -1) | |
277 | break; | |
278 | if (0 < sz && buf[sz - 1] == '\n') { | |
279 | sz--; | |
280 | buf[sz] = '\0'; | |
281 | } | |
282 | if (lines == vectors) { | |
283 | vectors *= 2; | |
047e2888 SK |
284 | if (IOV_MAX < vectors) |
285 | errx(EXIT_FAILURE, _("maximum input lines (%d) exceeded"), IOV_MAX); | |
4b670c01 SK |
286 | iovec = xrealloc(iovec, vectors * sizeof(struct iovec)); |
287 | } | |
288 | iovec[lines].iov_base = buf; | |
289 | iovec[lines].iov_len = sz; | |
290 | } | |
291 | ret = sd_journal_sendv(iovec, lines); | |
292 | for (n = 0; n < lines; n++) | |
293 | free(iovec[n].iov_base); | |
294 | free(iovec); | |
295 | return ret; | |
296 | } | |
297 | #endif | |
298 | ||
46ee14df | 299 | static char *xgetlogin(void) |
019b9702 SK |
300 | { |
301 | char *cp; | |
302 | struct passwd *pw; | |
303 | ||
304 | if (!(cp = getlogin()) || !*cp) | |
305 | cp = (pw = getpwuid(geteuid()))? pw->pw_name : "<someone>"; | |
306 | return cp; | |
307 | } | |
308 | ||
46ee14df | 309 | static void syslog_rfc3164(const struct logger_ctl *ctl, const char *msg) |
195c3603 | 310 | { |
77c3bd5b | 311 | char *buf, pid[30], *cp, *tp, *hostname, *dot; |
d8b616c2 | 312 | time_t now; |
852feb72 | 313 | int len; |
d8b616c2 | 314 | |
77c3bd5b | 315 | *pid = '\0'; |
cfa77d26 | 316 | if (ctl->fd < 0) |
c462a8ca | 317 | return; |
59c6ac0b KZ |
318 | if (ctl->pid) |
319 | snprintf(pid, sizeof(pid), "[%d]", ctl->pid); | |
852feb72 KZ |
320 | |
321 | cp = ctl->tag ? ctl->tag : xgetlogin(); | |
322 | ||
133bf77b SK |
323 | hostname = xgethostname(); |
324 | dot = strchr(hostname, '.'); | |
325 | if (dot) | |
326 | *dot = '\0'; | |
852feb72 | 327 | |
c462a8ca SK |
328 | time(&now); |
329 | tp = ctime(&now) + 4; | |
852feb72 KZ |
330 | |
331 | len = xasprintf(&buf, "<%d>%.15s %s %.200s%s: %.400s", | |
133bf77b | 332 | ctl->pri, tp, hostname, cp, pid, msg); |
852feb72 KZ |
333 | |
334 | if (write_all(ctl->fd, buf, len) < 0) | |
c462a8ca | 335 | warn(_("write failed")); |
35d36197 | 336 | if (ctl->stderr_printout) |
4288f9f1 | 337 | fprintf(stderr, "%s\n", buf); |
852feb72 KZ |
338 | |
339 | free(hostname); | |
340 | free(buf); | |
7eda085c KZ |
341 | } |
342 | ||
46ee14df | 343 | static void syslog_rfc5424(const struct logger_ctl *ctl, const char *msg) |
4de2e8a0 | 344 | { |
852feb72 | 345 | char *buf, *tag = NULL, *hostname = NULL; |
77c3bd5b | 346 | char pid[32], time[64], timeq[80]; |
87ee2658 | 347 | #ifdef HAVE_SYS_TIMEX_H |
4de2e8a0 | 348 | struct ntptimeval ntptv; |
87ee2658 | 349 | #endif |
4de2e8a0 SK |
350 | struct timeval tv; |
351 | struct tm *tm; | |
852feb72 | 352 | int len; |
4de2e8a0 | 353 | |
77c3bd5b | 354 | *pid = *time = *timeq = '\0'; |
4de2e8a0 SK |
355 | if (ctl->fd < 0) |
356 | return; | |
852feb72 | 357 | |
4de2e8a0 SK |
358 | if (ctl->rfc5424_time) { |
359 | gettimeofday(&tv, NULL); | |
360 | if ((tm = localtime(&tv.tv_sec)) != NULL) { | |
852feb72 KZ |
361 | char fmt[64]; |
362 | ||
2f267611 RG |
363 | const size_t i = strftime(fmt, sizeof(fmt), |
364 | " %Y-%m-%dT%H:%M:%S.%%06u%z ", tm); | |
365 | /* patch TZ info to comply with RFC3339 (we left SP at end) */ | |
366 | fmt[i-1] = fmt[i-2]; | |
367 | fmt[i-2] = fmt[i-3]; | |
368 | fmt[i-3] = ':'; | |
4de2e8a0 SK |
369 | snprintf(time, sizeof(time), fmt, tv.tv_usec); |
370 | } else | |
371 | err(EXIT_FAILURE, _("localtime() failed")); | |
852feb72 KZ |
372 | } |
373 | ||
4de2e8a0 SK |
374 | if (ctl->rfc5424_host) { |
375 | hostname = xgethostname(); | |
376 | /* Arbitrary looking 'if (var < strlen()) checks originate from | |
377 | * RFC 5424 - 6 Syslog Message Format definition. */ | |
378 | if (255 < strlen(hostname)) | |
379 | errx(EXIT_FAILURE, _("hostname '%s' is too long"), | |
380 | hostname); | |
852feb72 KZ |
381 | } |
382 | ||
383 | tag = ctl->tag ? ctl->tag : xgetlogin(); | |
384 | ||
4de2e8a0 SK |
385 | if (48 < strlen(tag)) |
386 | errx(EXIT_FAILURE, _("tag '%s' is too long"), tag); | |
852feb72 | 387 | |
59c6ac0b KZ |
388 | if (ctl->pid) |
389 | snprintf(pid, sizeof(pid), " %d", ctl->pid); | |
852feb72 | 390 | |
4de2e8a0 | 391 | if (ctl->rfc5424_tq) { |
87ee2658 | 392 | #ifdef HAVE_SYS_TIMEX_H |
4de2e8a0 SK |
393 | if (ntp_gettime(&ntptv) == TIME_OK) |
394 | snprintf(timeq, sizeof(timeq), | |
395 | " [timeQuality tzKnown=\"1\" isSynced=\"1\" syncAccuracy=\"%ld\"]", | |
396 | ntptv.maxerror); | |
397 | else | |
87ee2658 | 398 | #endif |
4de2e8a0 SK |
399 | snprintf(timeq, sizeof(timeq), |
400 | " [timeQuality tzKnown=\"1\" isSynced=\"0\"]"); | |
852feb72 KZ |
401 | } |
402 | ||
403 | len = xasprintf(&buf, "<%d>1%s%s%s %s -%s%s %s", ctl->pri, time, | |
404 | hostname ? " " : "", | |
405 | hostname ? hostname : "", | |
406 | tag, pid, timeq, msg); | |
407 | ||
408 | if (write_all(ctl->fd, buf, len) < 0) | |
4de2e8a0 | 409 | warn(_("write failed")); |
852feb72 | 410 | |
35d36197 | 411 | if (ctl->stderr_printout) |
4288f9f1 | 412 | fprintf(stderr, "%s\n", buf); |
852feb72 KZ |
413 | |
414 | free(hostname); | |
4de2e8a0 SK |
415 | free(buf); |
416 | } | |
417 | ||
418 | static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg) | |
419 | { | |
420 | char *in, *tok; | |
421 | ||
422 | in = optarg; | |
423 | while ((tok = strtok(in, ","))) { | |
424 | in = NULL; | |
425 | if (!strcmp(tok, "notime")) { | |
426 | ctl->rfc5424_time = 0; | |
427 | ctl->rfc5424_tq = 0; | |
428 | } else if (!strcmp(tok, "notq")) | |
429 | ctl->rfc5424_tq = 0; | |
430 | else if (!strcmp(tok, "nohost")) | |
431 | ctl->rfc5424_host = 0; | |
432 | else | |
433 | warnx(_("ignoring unknown option argument: %s"), tok); | |
434 | } | |
435 | } | |
436 | ||
d77dc29e SK |
437 | static int parse_unix_socket_errors_flags(char *optarg) |
438 | { | |
439 | if (!strcmp(optarg, "off")) | |
440 | return AF_UNIX_ERRORS_OFF; | |
441 | if (!strcmp(optarg, "on")) | |
442 | return AF_UNIX_ERRORS_ON; | |
443 | if (!strcmp(optarg, "auto")) | |
444 | return AF_UNIX_ERRORS_AUTO; | |
445 | warnx(_("invalid argument: %s: using automatic errors"), optarg); | |
446 | return AF_UNIX_ERRORS_AUTO; | |
447 | } | |
448 | ||
46ee14df | 449 | static void syslog_local(const struct logger_ctl *ctl, const char *msg) |
cfa77d26 | 450 | { |
1d575033 SK |
451 | char *buf, *tag; |
452 | char time[32], pid[32]; | |
453 | struct timeval tv; | |
454 | struct tm *tm; | |
1d575033 SK |
455 | int len; |
456 | ||
457 | gettimeofday(&tv, NULL); | |
458 | tm = localtime(&tv.tv_sec); | |
459 | strftime(time, sizeof(time), "%h %e %T", tm); | |
460 | ||
461 | tag = ctl->tag ? ctl->tag : program_invocation_short_name; | |
462 | ||
59c6ac0b KZ |
463 | if (ctl->pid) |
464 | snprintf(pid, sizeof(pid), "[%d]", ctl->pid); | |
1d575033 SK |
465 | else |
466 | pid[0] = '\0'; | |
467 | ||
468 | len = xasprintf(&buf, "<%d>%s %s%s: %s", ctl->pri, time, tag, pid, msg); | |
469 | if (write_all(ctl->fd, buf, len) < 0) | |
470 | warn(_("write failed")); | |
35d36197 | 471 | if (ctl->stderr_printout) |
1d575033 SK |
472 | fprintf(stderr, "%s\n", buf); |
473 | free(buf); | |
cfa77d26 SK |
474 | } |
475 | ||
c68a1cb4 SK |
476 | static void logger_open(struct logger_ctl *ctl) |
477 | { | |
478 | if (ctl->server) { | |
479 | ctl->fd = inet_socket(ctl->server, ctl->port, ctl->socket_type); | |
4de2e8a0 SK |
480 | if (!ctl->syslogfp) |
481 | ctl->syslogfp = syslog_rfc5424; | |
c68a1cb4 SK |
482 | return; |
483 | } | |
484 | if (ctl->unix_socket) { | |
d77dc29e | 485 | ctl->fd = unix_socket(ctl, ctl->unix_socket, ctl->socket_type); |
4de2e8a0 SK |
486 | if (!ctl->syslogfp) |
487 | ctl->syslogfp = syslog_rfc5424; | |
c68a1cb4 SK |
488 | return; |
489 | } | |
52a49e9a | 490 | ctl->fd = unix_socket(ctl, _PATH_DEVLOG, ctl->socket_type); |
c68a1cb4 SK |
491 | ctl->syslogfp = syslog_local; |
492 | } | |
493 | ||
46ee14df | 494 | static void logger_command_line(const struct logger_ctl *ctl, char **argv) |
c68a1cb4 SK |
495 | { |
496 | char buf[4096]; | |
497 | char *p = buf; | |
498 | const char *endp = buf + sizeof(buf) - 2; | |
499 | size_t len; | |
500 | ||
501 | while (*argv) { | |
502 | len = strlen(*argv); | |
503 | if (endp < p + len && p != buf) { | |
504 | ctl->syslogfp(ctl, buf); | |
505 | p = buf; | |
506 | } | |
507 | if (sizeof(buf) - 1 < len) { | |
508 | ctl->syslogfp(ctl, *argv++); | |
509 | continue; | |
510 | } | |
511 | if (p != buf) | |
512 | *p++ = ' '; | |
513 | memmove(p, *argv++, len); | |
514 | *(p += len) = '\0'; | |
515 | } | |
516 | if (p != buf) | |
517 | ctl->syslogfp(ctl, buf); | |
518 | } | |
519 | ||
520 | static void logger_stdin(struct logger_ctl *ctl) | |
521 | { | |
522 | char *msg; | |
523 | int default_priority = ctl->pri; | |
524 | char buf[1024]; | |
525 | ||
526 | while (fgets(buf, sizeof(buf), stdin) != NULL) { | |
527 | int len = strlen(buf); | |
528 | ||
529 | /* some glibc versions are buggy, they add an additional | |
530 | * newline which is removed here. */ | |
531 | if (0 < len && buf[len - 1] == '\n') | |
532 | buf[len - 1] = '\0'; | |
533 | msg = buf; | |
534 | ctl->pri = default_priority; | |
535 | if (ctl->prio_prefix && msg[0] == '<') | |
536 | msg = get_prio_prefix(msg, &ctl->pri); | |
537 | ctl->syslogfp(ctl, msg); | |
538 | } | |
539 | } | |
540 | ||
46ee14df | 541 | static void logger_close(const struct logger_ctl *ctl) |
c68a1cb4 | 542 | { |
1d575033 SK |
543 | if (close(ctl->fd) != 0) |
544 | err(EXIT_FAILURE, _("close failed")); | |
c68a1cb4 SK |
545 | } |
546 | ||
b363e86d SK |
547 | static void __attribute__ ((__noreturn__)) usage(FILE *out) |
548 | { | |
925aa9e8 | 549 | fputs(USAGE_HEADER, out); |
4ce393f4 | 550 | fprintf(out, _(" %s [options] [<message>]\n"), program_invocation_short_name); |
2da49186 | 551 | |
451dbcfa BS |
552 | fputs(USAGE_SEPARATOR, out); |
553 | fputs(_("Enter messages into the system log.\n"), out); | |
554 | ||
925aa9e8 | 555 | fputs(USAGE_OPTIONS, out); |
3f51c10b SK |
556 | fputs(_(" -i log the logger command's PID\n"), out); |
557 | fputs(_(" --id[=<id>] log the given <id>, or otherwise the PID\n"), out); | |
d0e875ff KZ |
558 | fputs(_(" -f, --file <file> log the contents of this file\n"), out); |
559 | fputs(_(" -p, --priority <prio> mark given message with this priority\n"), out); | |
560 | fputs(_(" --prio-prefix look for a prefix on every line read from stdin\n"), out); | |
561 | fputs(_(" -s, --stderr output message to standard error as well\n"), out); | |
562 | fputs(_(" -t, --tag <tag> mark every line with this tag\n"), out); | |
563 | fputs(_(" -n, --server <name> write to this remote syslog server\n"), out); | |
564 | fputs(_(" -P, --port <number> use this UDP port\n"), out); | |
565 | fputs(_(" -T, --tcp use TCP only\n"), out); | |
566 | fputs(_(" -d, --udp use UDP only\n"), out); | |
567 | fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out); | |
568 | fputs(_(" --rfc5424[=<snip>] use the syslog protocol (the default);\n" | |
569 | " <snip> can be notime, or notq, and/or nohost\n"), out); | |
570 | fputs(_(" -u, --socket <socket> write to this Unix socket\n"), out); | |
d77dc29e SK |
571 | fputs(_(" --socket-errors[=<on|off|auto>]\n" |
572 | " print connection errors when using Unix sockets\n"), out); | |
ebff016a | 573 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
574 | fputs(_(" --journald[=<file>] write journald entry\n"), out); |
575 | #endif | |
925aa9e8 KZ |
576 | |
577 | fputs(USAGE_SEPARATOR, out); | |
578 | fputs(USAGE_HELP, out); | |
579 | fputs(USAGE_VERSION, out); | |
580 | fprintf(out, USAGE_MAN_TAIL("logger(1)")); | |
b363e86d SK |
581 | |
582 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
583 | } | |
584 | ||
6dbe3af9 KZ |
585 | /* |
586 | * logger -- read and log utility | |
587 | * | |
588 | * Reads from an input and arranges to write the result on the system | |
589 | * log. | |
590 | */ | |
195c3603 KZ |
591 | int main(int argc, char **argv) |
592 | { | |
cfa77d26 SK |
593 | struct logger_ctl ctl = { |
594 | .fd = -1, | |
59c6ac0b | 595 | .pid = 0, |
cfa77d26 | 596 | .pri = LOG_NOTICE, |
c68a1cb4 | 597 | .prio_prefix = 0, |
cfa77d26 | 598 | .tag = NULL, |
c68a1cb4 | 599 | .unix_socket = NULL, |
d77dc29e | 600 | .unix_socket_errors = 0, |
c68a1cb4 SK |
601 | .server = NULL, |
602 | .port = NULL, | |
4de2e8a0 SK |
603 | .socket_type = ALL_TYPES, |
604 | .rfc5424_time = 1, | |
605 | .rfc5424_tq = 1, | |
606 | .rfc5424_host = 1, | |
cfa77d26 | 607 | }; |
c68a1cb4 | 608 | int ch; |
3d9f4b1d | 609 | int stdout_reopened = 0; |
d77dc29e | 610 | int unix_socket_errors_mode = AF_UNIX_ERRORS_AUTO; |
ebff016a | 611 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
612 | FILE *jfd = NULL; |
613 | #endif | |
b363e86d | 614 | static const struct option longopts[] = { |
3f51c10b | 615 | { "id", optional_argument, 0, OPT_ID }, |
b363e86d SK |
616 | { "stderr", no_argument, 0, 's' }, |
617 | { "file", required_argument, 0, 'f' }, | |
618 | { "priority", required_argument, 0, 'p' }, | |
619 | { "tag", required_argument, 0, 't' }, | |
620 | { "socket", required_argument, 0, 'u' }, | |
d77dc29e | 621 | { "socket-errors", required_argument, 0, OPT_SOCKET_ERRORS }, |
b363e86d | 622 | { "udp", no_argument, 0, 'd' }, |
68265d07 | 623 | { "tcp", no_argument, 0, 'T' }, |
b363e86d SK |
624 | { "server", required_argument, 0, 'n' }, |
625 | { "port", required_argument, 0, 'P' }, | |
626 | { "version", no_argument, 0, 'V' }, | |
627 | { "help", no_argument, 0, 'h' }, | |
98920f80 | 628 | { "prio-prefix", no_argument, 0, OPT_PRIO_PREFIX }, |
4de2e8a0 SK |
629 | { "rfc3164", no_argument, 0, OPT_RFC3164 }, |
630 | { "rfc5424", optional_argument, 0, OPT_RFC5424 }, | |
ebff016a | 631 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
632 | { "journald", optional_argument, 0, OPT_JOURNALD }, |
633 | #endif | |
b363e86d SK |
634 | { NULL, 0, 0, 0 } |
635 | }; | |
7eda085c KZ |
636 | |
637 | setlocale(LC_ALL, ""); | |
638 | bindtextdomain(PACKAGE, LOCALEDIR); | |
639 | textdomain(PACKAGE); | |
c05a80ca | 640 | atexit(close_stdout); |
6dbe3af9 | 641 | |
3f51c10b | 642 | while ((ch = getopt_long(argc, argv, "f:ip:st:u:dTn:P:Vh", |
49999d6a | 643 | longopts, NULL)) != -1) { |
98920f80 | 644 | switch (ch) { |
6dbe3af9 | 645 | case 'f': /* file to log */ |
49999d6a | 646 | if (freopen(optarg, "r", stdin) == NULL) |
3d9f4b1d SK |
647 | err(EXIT_FAILURE, _("file %s"), optarg); |
648 | stdout_reopened = 1; | |
6dbe3af9 KZ |
649 | break; |
650 | case 'i': /* log process id also */ | |
3f51c10b SK |
651 | ctl.pid = getpid(); |
652 | break; | |
653 | case OPT_ID: | |
aab5b444 | 654 | if (optarg) { |
e598686d KZ |
655 | const char *p = optarg; |
656 | ||
657 | if (*p == '=') | |
658 | p++; | |
59c6ac0b KZ |
659 | ctl.pid = strtoul_or_err(optarg, _("failed to parse id")); |
660 | } else | |
661 | ctl.pid = getpid(); | |
6dbe3af9 KZ |
662 | break; |
663 | case 'p': /* priority */ | |
cfa77d26 | 664 | ctl.pri = pencode(optarg); |
6dbe3af9 KZ |
665 | break; |
666 | case 's': /* log to standard error */ | |
35d36197 | 667 | ctl.stderr_printout = 1; |
6dbe3af9 KZ |
668 | break; |
669 | case 't': /* tag */ | |
cfa77d26 | 670 | ctl.tag = optarg; |
6dbe3af9 | 671 | break; |
7eda085c | 672 | case 'u': /* unix socket */ |
c68a1cb4 | 673 | ctl.unix_socket = optarg; |
7eda085c | 674 | break; |
66ee8158 | 675 | case 'd': |
c68a1cb4 | 676 | ctl.socket_type = TYPE_UDP; |
68265d07 SK |
677 | break; |
678 | case 'T': | |
c68a1cb4 | 679 | ctl.socket_type = TYPE_TCP; |
66ee8158 | 680 | break; |
68265d07 | 681 | case 'n': |
c68a1cb4 | 682 | ctl.server = optarg; |
912d6b98 | 683 | break; |
68265d07 | 684 | case 'P': |
c68a1cb4 | 685 | ctl.port = optarg; |
912d6b98 | 686 | break; |
b363e86d | 687 | case 'V': |
e421313d | 688 | printf(UTIL_LINUX_VERSION); |
b363e86d SK |
689 | exit(EXIT_SUCCESS); |
690 | case 'h': | |
691 | usage(stdout); | |
98920f80 | 692 | case OPT_PRIO_PREFIX: |
c68a1cb4 | 693 | ctl.prio_prefix = 1; |
98920f80 | 694 | break; |
4de2e8a0 SK |
695 | case OPT_RFC3164: |
696 | ctl.syslogfp = syslog_rfc3164; | |
697 | break; | |
698 | case OPT_RFC5424: | |
699 | ctl.syslogfp = syslog_rfc5424; | |
700 | if (optarg) | |
701 | parse_rfc5424_flags(&ctl, optarg); | |
702 | break; | |
ebff016a | 703 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
704 | case OPT_JOURNALD: |
705 | if (optarg) { | |
706 | jfd = fopen(optarg, "r"); | |
707 | if (!jfd) | |
708 | err(EXIT_FAILURE, _("cannot open %s"), | |
709 | optarg); | |
710 | } else | |
711 | jfd = stdin; | |
712 | break; | |
713 | #endif | |
d77dc29e SK |
714 | case OPT_SOCKET_ERRORS: |
715 | unix_socket_errors_mode = parse_unix_socket_errors_flags(optarg); | |
716 | break; | |
6dbe3af9 KZ |
717 | case '?': |
718 | default: | |
b363e86d | 719 | usage(stderr); |
6dbe3af9 | 720 | } |
49999d6a | 721 | } |
6dbe3af9 KZ |
722 | argc -= optind; |
723 | argv += optind; | |
3d9f4b1d SK |
724 | if (stdout_reopened && argc) |
725 | warnx(_("--file <file> and <message> are mutually exclusive, message is ignored")); | |
ebff016a | 726 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
727 | if (jfd) { |
728 | int ret = journald_entry(jfd); | |
729 | if (stdin != jfd) | |
730 | fclose(jfd); | |
047e2888 | 731 | if (ret) |
54fefa07 | 732 | errx(EXIT_FAILURE, _("journald entry could not be written")); |
047e2888 | 733 | return EXIT_SUCCESS; |
4b670c01 SK |
734 | } |
735 | #endif | |
d77dc29e SK |
736 | switch (unix_socket_errors_mode) { |
737 | case AF_UNIX_ERRORS_OFF: | |
738 | ctl.unix_socket_errors = 0; | |
739 | break; | |
740 | case AF_UNIX_ERRORS_ON: | |
741 | ctl.unix_socket_errors = 1; | |
742 | break; | |
743 | case AF_UNIX_ERRORS_AUTO: | |
744 | #ifdef HAVE_LIBSYSTEMD | |
745 | ctl.unix_socket_errors = sd_booted(); | |
746 | #else | |
747 | ctl.unix_socket_errors = 0; | |
748 | #endif | |
749 | break; | |
750 | default: | |
751 | abort(); | |
752 | } | |
c68a1cb4 SK |
753 | logger_open(&ctl); |
754 | if (0 < argc) | |
755 | logger_command_line(&ctl, argv); | |
7eda085c | 756 | else |
c68a1cb4 SK |
757 | /* Note. --file <arg> reopens stdin making the below |
758 | * function to be used for file inputs. */ | |
759 | logger_stdin(&ctl); | |
760 | logger_close(&ctl); | |
49999d6a | 761 | return EXIT_SUCCESS; |
6dbe3af9 | 762 | } |