/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
v2.2
src/logger.c
516 строк
11 KB
Simon Josefsson
logger: Code for non-getaddrinfo removed.
26 июл 2021, 08:19
Не верифицирован
26 июл 2021, 08:19
1965afb
Код
Авторство
О чём код?
/* Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'. */ #include <config.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/uio.h> #include <netinet/in.h> #include <sys/un.h> #include <netdb.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <time.h> #include <pwd.h> #include <argp.h> #include <libinetutils.h> #include <progname.h> #include <ctype.h> #include <error.h> #include <xalloc.h> #include <inttostr.h> #include <unused-parameter.h> #define SYSLOG_NAMES #include <syslog.h> #ifndef HAVE_SYSLOG_INTERNAL # include "logprio.h" #endif #define MAKE_PRI(fac,pri) (((fac) & LOG_FACMASK) | ((pri) & LOG_PRIMASK)) static char *tag = NULL; static int logflags = 0; static int pri = MAKE_PRI (LOG_USER, LOG_NOTICE); /* Cf. parse_level */ /* Only one of `host' and `unixsock' will be non-NULL * once option parsing has been completed. */ static char *host = PATH_LOG; static char *unixsock = NULL; static char *source; static char *pidstr; #if HAVE_IPV6 static int host_family = AF_UNSPEC; #else /* Fall back to only IPv4. */ static int host_family = AF_INET; #endif /* !HAVE_IPV6 */ int decode (char *name, CODE *codetab, const char *what) { CODE *cp; if (isdigit (*name)) { char *p; unsigned long n = strtoul (name, &p, 0); /* For portability reasons, a numerical facility is entered * as a decimal integer, shifted left by three binary bits. * Any overflow check must adapt to this fact. * For the purpose of remote logging from a local system, * tests based on LOG_NFACILITIES are insufficient, as a * remote system may well distinguish more facilities than * the local system does! */ if (*p || n > LOG_FACMASK) /* Includes range errors. */ error (EXIT_FAILURE, 0, "invalid %s number: %s", what, name); return n; } for (cp = codetab; cp->c_name; cp++) { if (strcasecmp (name, cp->c_name) == 0) return cp->c_val; } error (EXIT_FAILURE, 0, "unknown %s name: %s", what, name); return -1; /* to pacify gcc */ } int parse_level (char *str) { char *p; int fac, prio = LOG_NOTICE; /* Default priority! */ p = strchr (str, '.'); if (p) *p++ = 0; fac = decode (str, (CODE *) facilitynames, "facility"); if (p) prio = decode (p, (CODE *) prioritynames, "priority"); return MAKE_PRI (fac, prio); } union logger_sockaddr { struct sockaddr sa; struct sockaddr_in sinet; #if HAVE_IPV6 struct sockaddr_in6 sinet6; #endif struct sockaddr_un sunix; }; int fd; static void open_socket (void) { union logger_sockaddr sockaddr; socklen_t socklen; int family; int ret; /* A UNIX socket name can be specified in two ways. * Zero length of `unixsock' is handled automatically. */ if ((host != NULL && strchr (host, '/')) || unixsock != NULL) { size_t len; /* Copy `unixsock' to `host' if necessary. * There is no need to differentiate them. */ if (unixsock) host = unixsock; len = strlen (host); if (len >= sizeof sockaddr.sunix.sun_path) error (EXIT_FAILURE, 0, "UNIX socket name too long"); strcpy (sockaddr.sunix.sun_path, host); sockaddr.sunix.sun_family = AF_UNIX; family = PF_UNIX; socklen = sizeof (sockaddr.sunix); } else { struct addrinfo hints, *ai, *res; char *p; #if HAVE_IPV6 /* Bare, numeric IPv6 addresses must be contained * in brackets in order that an appended port not * be read by mistake. */ if (*host == '[') { ++host; p = strchr (host, ']'); if (p) { *p++ = '\0'; if (*p == ':') ++p; else p = NULL; } } else { /* When no bracket was detected, then seek the * right-most colon character in order to correctly * parse IPv6 addresses. */ p = strrchr (host, ':'); if (p) *p++ = 0; } #else /* !HAVE_IPV6 */ p = strchr (host, ':'); if (p) *p++ = 0; #endif /* !HAVE_IPV6 */ if (!p) p = "syslog"; memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_DGRAM; /* This falls back to AF_INET if compilation * was made with !HAVE_IPV6. */ hints.ai_family = host_family; /* The complete handshake is attempted within * a single while-loop, since the answers from * getaddrinfo() need to be checked in detail. */ ret = getaddrinfo (host, p, &hints, &res); if (ret < 0) error (EXIT_FAILURE, 0, "%s:%s, %s", host, p, gai_strerror(ret)); for (ai = res; ai; ai = ai->ai_next) { fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (fd < 0) continue; if (source) { /* Resolver uses the same address family * as the already resolved target host. */ int ret; struct addrinfo tips, *a; memset (&tips, 0, sizeof (tips)); tips.ai_family = ai->ai_family; ret = getaddrinfo(source, NULL, &tips, &a); if (ret) { close (fd); continue; } if (bind (fd, a->ai_addr, a->ai_addrlen)) { freeaddrinfo (a); close (fd); continue; } freeaddrinfo (a); } if (connect (fd, ai->ai_addr, ai->ai_addrlen)) { close (fd); continue; } /* Socket standing, bound and connected. */ break; } if (res) freeaddrinfo (res); if (ai == NULL) error (EXIT_FAILURE, EADDRNOTAVAIL, "%s:%s", host, p); /* Existing socket can be returned now. * This handles AF_INET and AF_INET6. */ return; socklen = sizeof (sockaddr.sinet); } /* Execution arrives here for AF_UNIX. */ fd = socket (family, SOCK_DGRAM, 0); if (fd < 0) error (EXIT_FAILURE, errno, "cannot create socket"); if (family == PF_INET) { struct sockaddr_in s; s.sin_family = AF_INET; if (source) { if (inet_aton (source, (struct in_addr *) &s.sin_addr) != 1) error (EXIT_FAILURE, 0, "invalid source address"); } else s.sin_addr.s_addr = INADDR_ANY; s.sin_port = 0; if (bind(fd, (struct sockaddr*) &s, sizeof(s)) < 0) error (EXIT_FAILURE, errno, "cannot bind to source address"); } if (connect (fd, &sockaddr.sa, socklen)) error (EXIT_FAILURE, errno, "cannot connect"); } static void send_to_syslog (const char *msg) { char *pbuf; time_t now = time (NULL); size_t len; ssize_t rc; if (logflags & LOG_PID) rc = asprintf (&pbuf, "<%d>%.15s %s[%s]: %s", pri, ctime (&now) + 4, tag, pidstr, msg); else rc = asprintf (&pbuf, "<%d>%.15s %s: %s", pri, ctime (&now) + 4, tag, msg); if (rc == -1) error (EXIT_FAILURE, errno, "cannot format message"); len = strlen (pbuf); #ifdef LOG_PERROR if (logflags & LOG_PERROR) { struct iovec iov[2], *ioptr; size_t msglen = strlen (msg); ioptr = iov; ioptr->iov_base = (char*) msg; ioptr->iov_len = msglen; if (msg[msglen - 1] != '\n') { /* provide a newline */ ioptr++; ioptr->iov_base = (char *) "\n"; ioptr->iov_len = 1; } writev (fileno (stderr), iov, ioptr - iov + 1); } #endif /* LOG_PERROR */ rc = send (fd, pbuf, len, 0); free (pbuf); if (rc == -1) error (0, errno, "send failed"); else if (rc != (ssize_t) len) error (0, errno, "sent less bytes than expected (%lu vs. %lu)", (unsigned long) rc, (unsigned long) len); } const char args_doc[] = "[MESSAGE]"; const char doc[] = "Send messages to syslog"; static struct argp_option argp_options[] = { #define GRP 10 {"ipv4", '4', NULL, 0, "use IPv4 for logging to host", GRP }, {"ipv6", '6', NULL, 0, "use IPv6 with a host target", GRP }, { "host", 'h', "HOST", 0, "log to HOST instead of to the default " PATH_LOG, GRP }, { "unix", 'u', "SOCK", 0, "log to UNIX socket SOCK instead of " PATH_LOG, GRP }, { "source", 'S', "IP", 0, "set source IP address", GRP }, { "id", 'i', "PID", OPTION_ARG_OPTIONAL, "log the process id with every line", GRP }, #ifdef LOG_PERROR { "stderr", 's', NULL, 0, "copy the message to stderr", GRP }, #endif { "file", 'f', "FILE", 0, "log the content of FILE", GRP }, { "priority", 'p', "PRI", 0, "log with priority PRI", GRP }, { "tag", 't', "TAG", 0, "prepend every line with TAG", GRP }, #undef GRP {NULL, 0, NULL, 0, NULL, 0 } }; static error_t parse_opt (int key, char *arg, struct argp_state *state _GL_UNUSED_PARAMETER) { switch (key) { case '4': host_family = AF_INET; break; case '6': #if HAVE_IPV6 host_family = AF_INET6; break; #else /* !HAVE_IPV6 */ /* Print a warning but continue with IPv4. */ error (0, 0, "warning: Falling back to IPv4, " "since IPv6 is disabled"); /* AF_INET is set by default in this case. */ break; #endif /* !HAVE_IPV6 */ case 'h': host = arg; unixsock = NULL; /* Erase any previous `-u'. */ break; case 'u': unixsock = arg; host = NULL; /* Erase previous `-h'. */ break; case 'S': source = arg; break; case 'i': logflags |= LOG_PID; if (arg) pidstr = arg; else { char buf[INT_BUFSIZE_BOUND (uintmax_t)]; arg = umaxtostr (getpid (), buf); pidstr = xstrdup (arg); } break; #ifdef LOG_PERROR case 's': logflags |= LOG_PERROR; break; #endif /* LOG_PERROR */ case 'f': if (strcmp (arg, "-") && freopen (arg, "r", stdin) == NULL) error (EXIT_FAILURE, errno, "%s", arg); break; case 'p': pri = parse_level (arg); break; case 't': tag = arg; break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = {argp_options, parse_opt, args_doc, doc, NULL, NULL, NULL}; const char *program_authors[] = { "Sergey Poznyakoff", NULL }; int main (int argc, char *argv[]) { int index; char *buf = NULL; set_program_name (argv[0]); iu_argp_init ("logger", program_authors); argp_parse (&argp, argc, argv, 0, &index, NULL); argc -= index; argv += index; if (!tag) { tag = getenv ("USER"); if (!tag) { struct passwd *pw = getpwuid (getuid ()); if (pw) tag = xstrdup (pw->pw_name); else tag = xstrdup ("none"); } } open_socket (); if (argc > 0) { int i; size_t len = 0; char *p; for (i = 0; i < argc; i++) len += strlen (argv[i]) + 1; buf = xmalloc (len); for (i = 0, p = buf; i < argc; i++) { len = strlen (argv[i]); memcpy (p, argv[i], len); p += len; *p++ = ' '; } p[-1] = 0; send_to_syslog (buf); } else { size_t size = 0; while (getline (&buf, &size, stdin) > 0) send_to_syslog (buf); } free (buf); exit (EXIT_SUCCESS); }