]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/logger/logger.c
Import DTS files from Linux 5.2
[FreeBSD/FreeBSD.git] / usr.bin / logger / logger.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1983, 1993\n\
35         The Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37
38 #if 0
39 #ifndef lint
40 static char sccsid[] = "@(#)logger.c    8.1 (Berkeley) 6/6/93";
41 #endif /* not lint */
42 #endif
43
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46
47 #include <sys/param.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50
51 #include <ctype.h>
52 #include <err.h>
53 #include <netdb.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <time.h>
58 #include <unistd.h>
59
60 #define SYSLOG_NAMES
61 #include <syslog.h>
62
63 #define sstosa(ss)      ((struct sockaddr *)(void *)ss)
64
65 struct socks {
66         int sk_sock;
67         int sk_addrlen;
68         struct sockaddr_storage sk_addr;
69 };
70
71 static int      decode(char *, const CODE *);
72 static int      pencode(char *);
73 static ssize_t  socksetup(const char *, const char *, const char *,
74                     struct socks **);
75 static void     logmessage(int, const char *, const char *, const char *,
76                     struct socks *, ssize_t, const char *);
77 static void     usage(void);
78
79 #ifdef INET6
80 static int family = PF_UNSPEC;  /* protocol family (IPv4, IPv6 or both) */
81 #else
82 static int family = PF_INET;    /* protocol family (IPv4 only) */
83 #endif
84 static int send_to_all = 0;     /* send message to all IPv4/IPv6 addresses */
85
86 /*
87  * logger -- read and log utility
88  *
89  *      Reads from an input and arranges to write the result on the system
90  *      log.
91  */
92 int
93 main(int argc, char *argv[])
94 {
95         struct socks *socks;
96         ssize_t nsock;
97         time_t now;
98         int ch, logflags, pri;
99         char *tag, *host, buf[1024], *timestamp, tbuf[26],
100             *hostname, hbuf[MAXHOSTNAMELEN];
101         const char *svcname, *src;
102
103         tag = NULL;
104         host = NULL;
105         hostname = NULL;
106         svcname = "syslog";
107         src = NULL;
108         socks = NULL;
109         pri = LOG_USER | LOG_NOTICE;
110         logflags = 0;
111         unsetenv("TZ");
112         while ((ch = getopt(argc, argv, "46Af:H:h:iP:p:S:st:")) != -1)
113                 switch((char)ch) {
114                 case '4':
115                         family = PF_INET;
116                         break;
117 #ifdef INET6
118                 case '6':
119                         family = PF_INET6;
120                         break;
121 #endif
122                 case 'A':
123                         send_to_all++;
124                         break;
125                 case 'f':               /* file to log */
126                         if (freopen(optarg, "r", stdin) == NULL)
127                                 err(1, "%s", optarg);
128                         setvbuf(stdin, 0, _IONBF, 0);
129                         break;
130                 case 'H':               /* hostname to set in message header */
131                         hostname = optarg;
132                         break;
133                 case 'h':               /* hostname to deliver to */
134                         host = optarg;
135                         break;
136                 case 'i':               /* log process id also */
137                         logflags |= LOG_PID;
138                         break;
139                 case 'P':               /* service name or port number */
140                         svcname = optarg;
141                         break;
142                 case 'p':               /* priority */
143                         pri = pencode(optarg);
144                         break;
145                 case 's':               /* log to standard error */
146                         logflags |= LOG_PERROR;
147                         break;
148                 case 'S':               /* source address */
149                         src = optarg;
150                         break;
151                 case 't':               /* tag */
152                         tag = optarg;
153                         break;
154                 case '?':
155                 default:
156                         usage();
157                 }
158         argc -= optind;
159         argv += optind;
160
161         if (host) {
162                 nsock = socksetup(src, host, svcname, &socks);
163                 if (nsock <= 0)
164                         errx(1, "socket");
165         } else {
166                 if (src)
167                         errx(1, "-h option is missing.");
168                 nsock = 0;
169         }
170
171         if (tag == NULL)
172                 tag = getlogin();
173         /* setup for logging */
174         if (host == NULL)
175                 openlog(tag, logflags, 0);
176         (void) fclose(stdout);
177
178         (void )time(&now);
179         (void )ctime_r(&now, tbuf);
180         tbuf[19] = '\0';
181         timestamp = tbuf + 4;
182
183         if (hostname == NULL) {
184                 hostname = hbuf;
185                 (void )gethostname(hbuf, MAXHOSTNAMELEN);
186                 *strchrnul(hostname, '.') = '\0';
187         }
188
189         /* log input line if appropriate */
190         if (argc > 0) {
191                 char *p, *endp;
192                 size_t len;
193
194                 for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
195                         len = strlen(*argv);
196                         if (p + len > endp && p > buf) {
197                                 logmessage(pri, timestamp, hostname, tag,
198                                     socks, nsock, buf);
199                                 p = buf;
200                         }
201                         if (len > sizeof(buf) - 1)
202                                 logmessage(pri, timestamp, hostname, tag,
203                                     socks, nsock, *argv++);
204                         else {
205                                 if (p != buf)
206                                         *p++ = ' ';
207                                 bcopy(*argv++, p, len);
208                                 *(p += len) = '\0';
209                         }
210                 }
211                 if (p != buf)
212                         logmessage(pri, timestamp, hostname, tag, socks, nsock,
213                             buf);
214         } else
215                 while (fgets(buf, sizeof(buf), stdin) != NULL)
216                         logmessage(pri, timestamp, hostname, tag, socks, nsock,
217                             buf);
218         exit(0);
219 }
220
221 static ssize_t
222 socksetup(const char *src, const char *dst, const char *svcname,
223         struct socks **socks)
224 {
225         struct addrinfo hints, *res, *res0;
226         struct sockaddr_storage *ss_src[AF_MAX];
227         struct socks *sk;
228         ssize_t nsock = 0;
229         int error, maxs;
230
231         memset(&ss_src[0], 0, sizeof(ss_src));
232         if (src) {
233                 char *p, *p0, *hs, *hbuf, *sbuf;
234
235                 hbuf = sbuf = NULL;
236                 p0 = p = strdup(src);
237                 if (p0 == NULL)
238                         err(1, "strdup failed");
239                 hs = p0;        /* point to search ":" */ 
240 #ifdef INET6
241                 /* -S option supports IPv6 addr in "[2001:db8::1]:service". */
242                 if (*p0 == '[') {
243                         p = strchr(p0, ']');
244                         if (p == NULL)
245                                 errx(1, "\"]\" not found in src addr");
246                         *p = '\0';
247                         /* hs points just after ']' (':' or '\0'). */
248                         hs = p + 1;
249                         /*
250                          * p points just after '[' while it points hs
251                          * in the case of [].
252                          */
253                         p = ((p0 + 1) == (hs - 1)) ? hs : p0 + 1;
254                 }
255 #endif
256                 if (*p != '\0') {
257                         /* (p == hs) means ":514" or "[]:514". */
258                         hbuf = (p == hs && *p == ':') ? NULL : p;
259                         p = strchr(hs, ':');
260                         if (p != NULL) {
261                                 *p = '\0';
262                                 sbuf = (*(p + 1) != '\0') ? p + 1 : NULL;
263                         }
264                 }
265                 hints = (struct addrinfo){
266                         .ai_family = family,
267                         .ai_socktype = SOCK_DGRAM,
268                         .ai_flags = AI_PASSIVE
269                 };
270                 error = getaddrinfo(hbuf, sbuf, &hints, &res0);
271                 if (error)
272                         errx(1, "%s: %s", gai_strerror(error), src);
273                 for (res = res0; res; res = res->ai_next) {
274                         switch (res->ai_family) {
275                         case AF_INET:
276 #ifdef INET6
277                         case AF_INET6:
278 #endif
279                                 if (ss_src[res->ai_family] != NULL)
280                                         continue;
281                                 ss_src[res->ai_family] =
282                                     malloc(sizeof(struct sockaddr_storage));
283                                 if (ss_src[res->ai_family] == NULL)
284                                         err(1, "malloc failed");
285                                 memcpy(ss_src[res->ai_family], res->ai_addr,
286                                     res->ai_addrlen);
287                         }
288                 }
289                 freeaddrinfo(res0);
290                 free(p0);
291         }
292
293         /* resolve hostname */
294         hints = (struct addrinfo){
295                 .ai_family = family,
296                 .ai_socktype = SOCK_DGRAM
297         };
298         error = getaddrinfo(dst, svcname, &hints, &res0);
299         if (error == EAI_SERVICE) {
300                 warnx("%s/udp: unknown service", svcname);
301                 error = getaddrinfo(dst, "514", &hints, &res0);
302         }       
303         if (error)
304                 errx(1, "%s: %s", gai_strerror(error), dst);
305         /* count max number of sockets we may open */
306         maxs = 0;
307         for (res = res0; res; res = res->ai_next)
308                 maxs++;
309         sk = calloc(maxs, sizeof(*sk));
310         if (sk == NULL)
311                 errx(1, "couldn't allocate memory for sockets");
312         for (res = res0; res; res = res->ai_next) {
313                 int s;
314
315                 s = socket(res->ai_family, res->ai_socktype,
316                     res->ai_protocol);
317                 if (s < 0)
318                         continue;
319                 if (src && ss_src[res->ai_family] == NULL)
320                         errx(1, "address family mismatch");
321                         
322                 if (ss_src[res->ai_family]) {
323                         error = bind(s, sstosa(ss_src[res->ai_family]),
324                                     ss_src[res->ai_family]->ss_len);
325                         if (error < 0)
326                                 err(1, "bind");
327                 }
328                 sk[nsock] = (struct socks){
329                         .sk_addrlen = res->ai_addrlen,
330                         .sk_sock = s
331                 };
332                 memcpy(&sk[nsock].sk_addr, res->ai_addr, res->ai_addrlen);
333                 nsock++;
334         }
335         freeaddrinfo(res0);
336
337         *socks = sk;
338         return (nsock);
339 }
340
341 /*
342  *  Send the message to syslog, either on the local host, or on a remote host
343  */
344 static void
345 logmessage(int pri, const char *timestamp, const char *hostname,
346     const char *tag, struct socks *sk, ssize_t nsock, const char *buf)
347 {
348         char *line;
349         int len, i, lsent;
350
351         if (nsock == 0) {
352                 syslog(pri, "%s", buf);
353                 return;
354         }
355         if ((len = asprintf(&line, "<%d>%s %s %s: %s", pri, timestamp,
356             hostname, tag, buf)) == -1)
357                 errx(1, "asprintf");
358
359         lsent = -1;
360         for (i = 0; i < nsock; i++) {
361                 lsent = sendto(sk[i].sk_sock, line, len, 0,
362                                sstosa(&sk[i].sk_addr), sk[i].sk_addrlen);
363                 if (lsent == len && !send_to_all)
364                         break;
365         }
366         if (lsent != len) {
367                 if (lsent == -1)
368                         warn("sendto");
369                 else
370                         warnx("sendto: short send - %d bytes", lsent);
371         }
372
373         free(line);
374 }
375
376 /*
377  *  Decode a symbolic name to a numeric value
378  */
379 static int
380 pencode(char *s)
381 {
382         char *save;
383         int fac, lev;
384
385         for (save = s; *s && *s != '.'; ++s);
386         if (*s) {
387                 *s = '\0';
388                 fac = decode(save, facilitynames);
389                 if (fac < 0)
390                         errx(1, "unknown facility name: %s", save);
391                 *s++ = '.';
392         }
393         else {
394                 fac = 0;
395                 s = save;
396         }
397         lev = decode(s, prioritynames);
398         if (lev < 0)
399                 errx(1, "unknown priority name: %s", save);
400         return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
401 }
402
403 static int
404 decode(char *name, const CODE *codetab)
405 {
406         const CODE *c;
407
408         if (isdigit(*name))
409                 return (atoi(name));
410
411         for (c = codetab; c->c_name; c++)
412                 if (!strcasecmp(name, c->c_name))
413                         return (c->c_val);
414
415         return (-1);
416 }
417
418 static void
419 usage(void)
420 {
421         (void)fprintf(stderr, "usage: %s\n",
422             "logger [-46Ais] [-f file] [-h host] [-P port] [-p pri] [-t tag]\n"
423             "              [-S addr:port] [message ...]"
424             );
425         exit(1);
426 }