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, &res); 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 } 427