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