/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/logger.c
515 строк
11 KB
Simon Josefsson
maint: Run 'make update-copyright'
20 янв 2026, 11:50
Не верифицирован
20 янв 2026, 11:50
1ce61cd
Код
Авторство
О чём код?
/* Copyright (C) 2009-2026 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 <attribute.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 (msglen > 0 && 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 MAYBE_UNUSED) { 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); }