]>
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; |
c95d3209 | 101 | char *unix_socket; /* -u <path> or default to _PATH_DEVLOG */ |
c68a1cb4 SK |
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 | ||
3070ca77 RG |
309 | |
310 | /* this creates a timestamp based on current time according to the | |
311 | * fine rules of RFC3164, most importantly it ensures in a portable | |
312 | * way that the month day is correctly written (with a SP instead | |
313 | * of a leading 0). The function uses a static buffer which is | |
314 | * overwritten on the next call (just like ctime() does). | |
315 | */ | |
316 | static const char * | |
317 | rfc3164_current_time(void) | |
318 | { | |
319 | static char time[32]; | |
320 | struct timeval tv; | |
321 | struct tm *tm; | |
322 | static char *monthnames[] = { "Jan", "Feb", "Mar", "Apr", | |
323 | "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; | |
324 | ||
325 | gettimeofday(&tv, NULL); | |
326 | tm = localtime(&tv.tv_sec); | |
327 | snprintf(time, sizeof(time),"%s %2d %2.2d:%2.2d:%2.2d", | |
328 | monthnames[tm->tm_mon], tm->tm_mday, | |
329 | tm->tm_hour, tm->tm_min, tm->tm_sec); | |
330 | return time; | |
331 | } | |
332 | ||
46ee14df | 333 | static void syslog_rfc3164(const struct logger_ctl *ctl, const char *msg) |
195c3603 | 334 | { |
3070ca77 | 335 | char *buf, pid[30], *cp, *hostname, *dot; |
d8b616c2 | 336 | time_t now; |
852feb72 | 337 | int len; |
d8b616c2 | 338 | |
77c3bd5b | 339 | *pid = '\0'; |
cfa77d26 | 340 | if (ctl->fd < 0) |
c462a8ca | 341 | return; |
59c6ac0b KZ |
342 | if (ctl->pid) |
343 | snprintf(pid, sizeof(pid), "[%d]", ctl->pid); | |
852feb72 KZ |
344 | |
345 | cp = ctl->tag ? ctl->tag : xgetlogin(); | |
346 | ||
133bf77b SK |
347 | hostname = xgethostname(); |
348 | dot = strchr(hostname, '.'); | |
349 | if (dot) | |
350 | *dot = '\0'; | |
852feb72 | 351 | |
852feb72 | 352 | len = xasprintf(&buf, "<%d>%.15s %s %.200s%s: %.400s", |
3070ca77 | 353 | ctl->pri, rfc3164_current_time(), hostname, cp, pid, msg); |
852feb72 KZ |
354 | |
355 | if (write_all(ctl->fd, buf, len) < 0) | |
c462a8ca | 356 | warn(_("write failed")); |
35d36197 | 357 | if (ctl->stderr_printout) |
4288f9f1 | 358 | fprintf(stderr, "%s\n", buf); |
852feb72 KZ |
359 | |
360 | free(hostname); | |
361 | free(buf); | |
7eda085c KZ |
362 | } |
363 | ||
46ee14df | 364 | static void syslog_rfc5424(const struct logger_ctl *ctl, const char *msg) |
4de2e8a0 | 365 | { |
852feb72 | 366 | char *buf, *tag = NULL, *hostname = NULL; |
77c3bd5b | 367 | char pid[32], time[64], timeq[80]; |
87ee2658 | 368 | #ifdef HAVE_SYS_TIMEX_H |
4de2e8a0 | 369 | struct ntptimeval ntptv; |
87ee2658 | 370 | #endif |
4de2e8a0 SK |
371 | struct timeval tv; |
372 | struct tm *tm; | |
852feb72 | 373 | int len; |
4de2e8a0 | 374 | |
77c3bd5b | 375 | *pid = *time = *timeq = '\0'; |
4de2e8a0 SK |
376 | if (ctl->fd < 0) |
377 | return; | |
852feb72 | 378 | |
4de2e8a0 SK |
379 | if (ctl->rfc5424_time) { |
380 | gettimeofday(&tv, NULL); | |
381 | if ((tm = localtime(&tv.tv_sec)) != NULL) { | |
852feb72 KZ |
382 | char fmt[64]; |
383 | ||
2f267611 RG |
384 | const size_t i = strftime(fmt, sizeof(fmt), |
385 | " %Y-%m-%dT%H:%M:%S.%%06u%z ", tm); | |
386 | /* patch TZ info to comply with RFC3339 (we left SP at end) */ | |
387 | fmt[i-1] = fmt[i-2]; | |
388 | fmt[i-2] = fmt[i-3]; | |
389 | fmt[i-3] = ':'; | |
4de2e8a0 SK |
390 | snprintf(time, sizeof(time), fmt, tv.tv_usec); |
391 | } else | |
392 | err(EXIT_FAILURE, _("localtime() failed")); | |
852feb72 KZ |
393 | } |
394 | ||
4de2e8a0 SK |
395 | if (ctl->rfc5424_host) { |
396 | hostname = xgethostname(); | |
397 | /* Arbitrary looking 'if (var < strlen()) checks originate from | |
398 | * RFC 5424 - 6 Syslog Message Format definition. */ | |
399 | if (255 < strlen(hostname)) | |
400 | errx(EXIT_FAILURE, _("hostname '%s' is too long"), | |
401 | hostname); | |
852feb72 KZ |
402 | } |
403 | ||
404 | tag = ctl->tag ? ctl->tag : xgetlogin(); | |
405 | ||
4de2e8a0 SK |
406 | if (48 < strlen(tag)) |
407 | errx(EXIT_FAILURE, _("tag '%s' is too long"), tag); | |
852feb72 | 408 | |
59c6ac0b KZ |
409 | if (ctl->pid) |
410 | snprintf(pid, sizeof(pid), " %d", ctl->pid); | |
852feb72 | 411 | |
4de2e8a0 | 412 | if (ctl->rfc5424_tq) { |
87ee2658 | 413 | #ifdef HAVE_SYS_TIMEX_H |
4de2e8a0 SK |
414 | if (ntp_gettime(&ntptv) == TIME_OK) |
415 | snprintf(timeq, sizeof(timeq), | |
416 | " [timeQuality tzKnown=\"1\" isSynced=\"1\" syncAccuracy=\"%ld\"]", | |
417 | ntptv.maxerror); | |
418 | else | |
87ee2658 | 419 | #endif |
4de2e8a0 SK |
420 | snprintf(timeq, sizeof(timeq), |
421 | " [timeQuality tzKnown=\"1\" isSynced=\"0\"]"); | |
852feb72 KZ |
422 | } |
423 | ||
424 | len = xasprintf(&buf, "<%d>1%s%s%s %s -%s%s %s", ctl->pri, time, | |
425 | hostname ? " " : "", | |
426 | hostname ? hostname : "", | |
427 | tag, pid, timeq, msg); | |
428 | ||
429 | if (write_all(ctl->fd, buf, len) < 0) | |
4de2e8a0 | 430 | warn(_("write failed")); |
852feb72 | 431 | |
35d36197 | 432 | if (ctl->stderr_printout) |
4288f9f1 | 433 | fprintf(stderr, "%s\n", buf); |
852feb72 KZ |
434 | |
435 | free(hostname); | |
4de2e8a0 SK |
436 | free(buf); |
437 | } | |
438 | ||
439 | static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg) | |
440 | { | |
441 | char *in, *tok; | |
442 | ||
443 | in = optarg; | |
444 | while ((tok = strtok(in, ","))) { | |
445 | in = NULL; | |
446 | if (!strcmp(tok, "notime")) { | |
447 | ctl->rfc5424_time = 0; | |
448 | ctl->rfc5424_tq = 0; | |
449 | } else if (!strcmp(tok, "notq")) | |
450 | ctl->rfc5424_tq = 0; | |
451 | else if (!strcmp(tok, "nohost")) | |
452 | ctl->rfc5424_host = 0; | |
453 | else | |
454 | warnx(_("ignoring unknown option argument: %s"), tok); | |
455 | } | |
456 | } | |
457 | ||
d77dc29e SK |
458 | static int parse_unix_socket_errors_flags(char *optarg) |
459 | { | |
460 | if (!strcmp(optarg, "off")) | |
461 | return AF_UNIX_ERRORS_OFF; | |
462 | if (!strcmp(optarg, "on")) | |
463 | return AF_UNIX_ERRORS_ON; | |
464 | if (!strcmp(optarg, "auto")) | |
465 | return AF_UNIX_ERRORS_AUTO; | |
466 | warnx(_("invalid argument: %s: using automatic errors"), optarg); | |
467 | return AF_UNIX_ERRORS_AUTO; | |
468 | } | |
469 | ||
46ee14df | 470 | static void syslog_local(const struct logger_ctl *ctl, const char *msg) |
cfa77d26 | 471 | { |
1d575033 | 472 | char *buf, *tag; |
3070ca77 | 473 | char pid[32]; |
1d575033 SK |
474 | int len; |
475 | ||
1d575033 SK |
476 | tag = ctl->tag ? ctl->tag : program_invocation_short_name; |
477 | ||
59c6ac0b KZ |
478 | if (ctl->pid) |
479 | snprintf(pid, sizeof(pid), "[%d]", ctl->pid); | |
1d575033 SK |
480 | else |
481 | pid[0] = '\0'; | |
482 | ||
3070ca77 RG |
483 | len = xasprintf(&buf, "<%d>%s %s%s: %s", ctl->pri, rfc3164_current_time(), |
484 | tag, pid, msg); | |
1d575033 SK |
485 | if (write_all(ctl->fd, buf, len) < 0) |
486 | warn(_("write failed")); | |
35d36197 | 487 | if (ctl->stderr_printout) |
1d575033 SK |
488 | fprintf(stderr, "%s\n", buf); |
489 | free(buf); | |
cfa77d26 SK |
490 | } |
491 | ||
c68a1cb4 SK |
492 | static void logger_open(struct logger_ctl *ctl) |
493 | { | |
494 | if (ctl->server) { | |
495 | ctl->fd = inet_socket(ctl->server, ctl->port, ctl->socket_type); | |
4de2e8a0 SK |
496 | if (!ctl->syslogfp) |
497 | ctl->syslogfp = syslog_rfc5424; | |
c68a1cb4 SK |
498 | return; |
499 | } | |
7dc20804 RG |
500 | if (!ctl->unix_socket) |
501 | ctl->unix_socket = _PATH_DEVLOG; | |
502 | ||
503 | ctl->fd = unix_socket(ctl, ctl->unix_socket, ctl->socket_type); | |
504 | if (!ctl->syslogfp) | |
505 | ctl->syslogfp = syslog_local; | |
c68a1cb4 SK |
506 | } |
507 | ||
46ee14df | 508 | static void logger_command_line(const struct logger_ctl *ctl, char **argv) |
c68a1cb4 SK |
509 | { |
510 | char buf[4096]; | |
511 | char *p = buf; | |
512 | const char *endp = buf + sizeof(buf) - 2; | |
513 | size_t len; | |
514 | ||
515 | while (*argv) { | |
516 | len = strlen(*argv); | |
517 | if (endp < p + len && p != buf) { | |
518 | ctl->syslogfp(ctl, buf); | |
519 | p = buf; | |
520 | } | |
521 | if (sizeof(buf) - 1 < len) { | |
522 | ctl->syslogfp(ctl, *argv++); | |
523 | continue; | |
524 | } | |
525 | if (p != buf) | |
526 | *p++ = ' '; | |
527 | memmove(p, *argv++, len); | |
528 | *(p += len) = '\0'; | |
529 | } | |
530 | if (p != buf) | |
531 | ctl->syslogfp(ctl, buf); | |
532 | } | |
533 | ||
534 | static void logger_stdin(struct logger_ctl *ctl) | |
535 | { | |
536 | char *msg; | |
537 | int default_priority = ctl->pri; | |
538 | char buf[1024]; | |
539 | ||
540 | while (fgets(buf, sizeof(buf), stdin) != NULL) { | |
541 | int len = strlen(buf); | |
542 | ||
543 | /* some glibc versions are buggy, they add an additional | |
544 | * newline which is removed here. */ | |
545 | if (0 < len && buf[len - 1] == '\n') | |
546 | buf[len - 1] = '\0'; | |
547 | msg = buf; | |
548 | ctl->pri = default_priority; | |
549 | if (ctl->prio_prefix && msg[0] == '<') | |
550 | msg = get_prio_prefix(msg, &ctl->pri); | |
551 | ctl->syslogfp(ctl, msg); | |
552 | } | |
553 | } | |
554 | ||
46ee14df | 555 | static void logger_close(const struct logger_ctl *ctl) |
c68a1cb4 | 556 | { |
1d575033 SK |
557 | if (close(ctl->fd) != 0) |
558 | err(EXIT_FAILURE, _("close failed")); | |
c68a1cb4 SK |
559 | } |
560 | ||
b363e86d SK |
561 | static void __attribute__ ((__noreturn__)) usage(FILE *out) |
562 | { | |
925aa9e8 | 563 | fputs(USAGE_HEADER, out); |
4ce393f4 | 564 | fprintf(out, _(" %s [options] [<message>]\n"), program_invocation_short_name); |
2da49186 | 565 | |
451dbcfa BS |
566 | fputs(USAGE_SEPARATOR, out); |
567 | fputs(_("Enter messages into the system log.\n"), out); | |
568 | ||
925aa9e8 | 569 | fputs(USAGE_OPTIONS, out); |
3f51c10b SK |
570 | fputs(_(" -i log the logger command's PID\n"), out); |
571 | fputs(_(" --id[=<id>] log the given <id>, or otherwise the PID\n"), out); | |
d0e875ff KZ |
572 | fputs(_(" -f, --file <file> log the contents of this file\n"), out); |
573 | fputs(_(" -p, --priority <prio> mark given message with this priority\n"), out); | |
574 | fputs(_(" --prio-prefix look for a prefix on every line read from stdin\n"), out); | |
575 | fputs(_(" -s, --stderr output message to standard error as well\n"), out); | |
576 | fputs(_(" -t, --tag <tag> mark every line with this tag\n"), out); | |
577 | fputs(_(" -n, --server <name> write to this remote syslog server\n"), out); | |
578 | fputs(_(" -P, --port <number> use this UDP port\n"), out); | |
579 | fputs(_(" -T, --tcp use TCP only\n"), out); | |
580 | fputs(_(" -d, --udp use UDP only\n"), out); | |
581 | fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out); | |
582 | fputs(_(" --rfc5424[=<snip>] use the syslog protocol (the default);\n" | |
583 | " <snip> can be notime, or notq, and/or nohost\n"), out); | |
584 | fputs(_(" -u, --socket <socket> write to this Unix socket\n"), out); | |
d77dc29e SK |
585 | fputs(_(" --socket-errors[=<on|off|auto>]\n" |
586 | " print connection errors when using Unix sockets\n"), out); | |
ebff016a | 587 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
588 | fputs(_(" --journald[=<file>] write journald entry\n"), out); |
589 | #endif | |
925aa9e8 KZ |
590 | |
591 | fputs(USAGE_SEPARATOR, out); | |
592 | fputs(USAGE_HELP, out); | |
593 | fputs(USAGE_VERSION, out); | |
594 | fprintf(out, USAGE_MAN_TAIL("logger(1)")); | |
b363e86d SK |
595 | |
596 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
597 | } | |
598 | ||
6dbe3af9 KZ |
599 | /* |
600 | * logger -- read and log utility | |
601 | * | |
602 | * Reads from an input and arranges to write the result on the system | |
603 | * log. | |
604 | */ | |
195c3603 KZ |
605 | int main(int argc, char **argv) |
606 | { | |
cfa77d26 SK |
607 | struct logger_ctl ctl = { |
608 | .fd = -1, | |
59c6ac0b | 609 | .pid = 0, |
cfa77d26 | 610 | .pri = LOG_NOTICE, |
c68a1cb4 | 611 | .prio_prefix = 0, |
cfa77d26 | 612 | .tag = NULL, |
c68a1cb4 | 613 | .unix_socket = NULL, |
d77dc29e | 614 | .unix_socket_errors = 0, |
c68a1cb4 SK |
615 | .server = NULL, |
616 | .port = NULL, | |
4de2e8a0 SK |
617 | .socket_type = ALL_TYPES, |
618 | .rfc5424_time = 1, | |
619 | .rfc5424_tq = 1, | |
620 | .rfc5424_host = 1, | |
cfa77d26 | 621 | }; |
c68a1cb4 | 622 | int ch; |
3d9f4b1d | 623 | int stdout_reopened = 0; |
d77dc29e | 624 | int unix_socket_errors_mode = AF_UNIX_ERRORS_AUTO; |
ebff016a | 625 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
626 | FILE *jfd = NULL; |
627 | #endif | |
b363e86d | 628 | static const struct option longopts[] = { |
3f51c10b | 629 | { "id", optional_argument, 0, OPT_ID }, |
b363e86d SK |
630 | { "stderr", no_argument, 0, 's' }, |
631 | { "file", required_argument, 0, 'f' }, | |
632 | { "priority", required_argument, 0, 'p' }, | |
633 | { "tag", required_argument, 0, 't' }, | |
634 | { "socket", required_argument, 0, 'u' }, | |
d77dc29e | 635 | { "socket-errors", required_argument, 0, OPT_SOCKET_ERRORS }, |
b363e86d | 636 | { "udp", no_argument, 0, 'd' }, |
68265d07 | 637 | { "tcp", no_argument, 0, 'T' }, |
b363e86d SK |
638 | { "server", required_argument, 0, 'n' }, |
639 | { "port", required_argument, 0, 'P' }, | |
640 | { "version", no_argument, 0, 'V' }, | |
641 | { "help", no_argument, 0, 'h' }, | |
98920f80 | 642 | { "prio-prefix", no_argument, 0, OPT_PRIO_PREFIX }, |
4de2e8a0 SK |
643 | { "rfc3164", no_argument, 0, OPT_RFC3164 }, |
644 | { "rfc5424", optional_argument, 0, OPT_RFC5424 }, | |
ebff016a | 645 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
646 | { "journald", optional_argument, 0, OPT_JOURNALD }, |
647 | #endif | |
b363e86d SK |
648 | { NULL, 0, 0, 0 } |
649 | }; | |
7eda085c KZ |
650 | |
651 | setlocale(LC_ALL, ""); | |
652 | bindtextdomain(PACKAGE, LOCALEDIR); | |
653 | textdomain(PACKAGE); | |
c05a80ca | 654 | atexit(close_stdout); |
6dbe3af9 | 655 | |
3f51c10b | 656 | while ((ch = getopt_long(argc, argv, "f:ip:st:u:dTn:P:Vh", |
49999d6a | 657 | longopts, NULL)) != -1) { |
98920f80 | 658 | switch (ch) { |
6dbe3af9 | 659 | case 'f': /* file to log */ |
49999d6a | 660 | if (freopen(optarg, "r", stdin) == NULL) |
3d9f4b1d SK |
661 | err(EXIT_FAILURE, _("file %s"), optarg); |
662 | stdout_reopened = 1; | |
6dbe3af9 KZ |
663 | break; |
664 | case 'i': /* log process id also */ | |
3f51c10b SK |
665 | ctl.pid = getpid(); |
666 | break; | |
667 | case OPT_ID: | |
aab5b444 | 668 | if (optarg) { |
e598686d KZ |
669 | const char *p = optarg; |
670 | ||
671 | if (*p == '=') | |
672 | p++; | |
59c6ac0b KZ |
673 | ctl.pid = strtoul_or_err(optarg, _("failed to parse id")); |
674 | } else | |
675 | ctl.pid = getpid(); | |
6dbe3af9 KZ |
676 | break; |
677 | case 'p': /* priority */ | |
cfa77d26 | 678 | ctl.pri = pencode(optarg); |
6dbe3af9 KZ |
679 | break; |
680 | case 's': /* log to standard error */ | |
35d36197 | 681 | ctl.stderr_printout = 1; |
6dbe3af9 KZ |
682 | break; |
683 | case 't': /* tag */ | |
cfa77d26 | 684 | ctl.tag = optarg; |
6dbe3af9 | 685 | break; |
7eda085c | 686 | case 'u': /* unix socket */ |
c68a1cb4 | 687 | ctl.unix_socket = optarg; |
7eda085c | 688 | break; |
66ee8158 | 689 | case 'd': |
c68a1cb4 | 690 | ctl.socket_type = TYPE_UDP; |
68265d07 SK |
691 | break; |
692 | case 'T': | |
c68a1cb4 | 693 | ctl.socket_type = TYPE_TCP; |
66ee8158 | 694 | break; |
68265d07 | 695 | case 'n': |
c68a1cb4 | 696 | ctl.server = optarg; |
912d6b98 | 697 | break; |
68265d07 | 698 | case 'P': |
c68a1cb4 | 699 | ctl.port = optarg; |
912d6b98 | 700 | break; |
b363e86d | 701 | case 'V': |
e421313d | 702 | printf(UTIL_LINUX_VERSION); |
b363e86d SK |
703 | exit(EXIT_SUCCESS); |
704 | case 'h': | |
705 | usage(stdout); | |
98920f80 | 706 | case OPT_PRIO_PREFIX: |
c68a1cb4 | 707 | ctl.prio_prefix = 1; |
98920f80 | 708 | break; |
4de2e8a0 SK |
709 | case OPT_RFC3164: |
710 | ctl.syslogfp = syslog_rfc3164; | |
711 | break; | |
712 | case OPT_RFC5424: | |
713 | ctl.syslogfp = syslog_rfc5424; | |
714 | if (optarg) | |
715 | parse_rfc5424_flags(&ctl, optarg); | |
716 | break; | |
ebff016a | 717 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
718 | case OPT_JOURNALD: |
719 | if (optarg) { | |
720 | jfd = fopen(optarg, "r"); | |
721 | if (!jfd) | |
722 | err(EXIT_FAILURE, _("cannot open %s"), | |
723 | optarg); | |
724 | } else | |
725 | jfd = stdin; | |
726 | break; | |
727 | #endif | |
d77dc29e SK |
728 | case OPT_SOCKET_ERRORS: |
729 | unix_socket_errors_mode = parse_unix_socket_errors_flags(optarg); | |
730 | break; | |
6dbe3af9 KZ |
731 | case '?': |
732 | default: | |
b363e86d | 733 | usage(stderr); |
6dbe3af9 | 734 | } |
49999d6a | 735 | } |
6dbe3af9 KZ |
736 | argc -= optind; |
737 | argv += optind; | |
3d9f4b1d SK |
738 | if (stdout_reopened && argc) |
739 | warnx(_("--file <file> and <message> are mutually exclusive, message is ignored")); | |
ebff016a | 740 | #ifdef HAVE_LIBSYSTEMD |
4b670c01 SK |
741 | if (jfd) { |
742 | int ret = journald_entry(jfd); | |
743 | if (stdin != jfd) | |
744 | fclose(jfd); | |
047e2888 | 745 | if (ret) |
54fefa07 | 746 | errx(EXIT_FAILURE, _("journald entry could not be written")); |
047e2888 | 747 | return EXIT_SUCCESS; |
4b670c01 SK |
748 | } |
749 | #endif | |
d77dc29e SK |
750 | switch (unix_socket_errors_mode) { |
751 | case AF_UNIX_ERRORS_OFF: | |
752 | ctl.unix_socket_errors = 0; | |
753 | break; | |
754 | case AF_UNIX_ERRORS_ON: | |
755 | ctl.unix_socket_errors = 1; | |
756 | break; | |
757 | case AF_UNIX_ERRORS_AUTO: | |
758 | #ifdef HAVE_LIBSYSTEMD | |
759 | ctl.unix_socket_errors = sd_booted(); | |
760 | #else | |
761 | ctl.unix_socket_errors = 0; | |
762 | #endif | |
763 | break; | |
764 | default: | |
765 | abort(); | |
766 | } | |
c68a1cb4 SK |
767 | logger_open(&ctl); |
768 | if (0 < argc) | |
769 | logger_command_line(&ctl, argv); | |
7eda085c | 770 | else |
c68a1cb4 SK |
771 | /* Note. --file <arg> reopens stdin making the below |
772 | * function to be used for file inputs. */ | |
773 | logger_stdin(&ctl); | |
774 | logger_close(&ctl); | |
49999d6a | 775 | return EXIT_SUCCESS; |
6dbe3af9 | 776 | } |