mirror of
https://github.com/samba-team/samba.git
synced 2025-01-13 13:18:06 +03:00
8ed3ff456c
This has most of the advantages of the old logd with none of the complexity of the extra process. There are several good syslog implementations that can listen on the UDP port. Signed-off-by: Martin Schwenke <martin@meltin.net> Reviewed-by: Amitay Isaacs <amitay@gmail.com>
263 lines
6.4 KiB
C
263 lines
6.4 KiB
C
/*
|
|
ctdb logging code - syslog backend
|
|
|
|
Copyright (C) Andrew Tridgell 2008
|
|
Copyright (C) Martin Schwenke 2014
|
|
|
|
This program 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.
|
|
|
|
This program 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 "replace.h"
|
|
#include "system/network.h"
|
|
#include "system/syslog.h"
|
|
#include "lib/util/debug.h"
|
|
#include "lib/util/blocking.h"
|
|
#include "ctdb_logging.h"
|
|
|
|
/* Linux and FreeBSD define this appropriately - try good old /dev/log
|
|
* for anything that doesn't... */
|
|
#ifndef _PATH_LOG
|
|
#define _PATH_LOG "/dev/log"
|
|
#endif
|
|
|
|
#define CTDB_LOG_SYSLOG_PREFIX "syslog"
|
|
#define CTDB_SYSLOG_FACILITY LOG_USER
|
|
|
|
struct ctdb_syslog_sock_state {
|
|
int fd;
|
|
const char *app_name;
|
|
};
|
|
|
|
/**********************************************************************/
|
|
|
|
static int ctdb_debug_to_syslog_level(int dbglevel)
|
|
{
|
|
int level;
|
|
|
|
switch (dbglevel) {
|
|
case DEBUG_ERR:
|
|
level = LOG_ERR;
|
|
break;
|
|
case DEBUG_WARNING:
|
|
level = LOG_WARNING;
|
|
break;
|
|
case DEBUG_NOTICE:
|
|
level = LOG_NOTICE;
|
|
break;
|
|
case DEBUG_INFO:
|
|
level = LOG_INFO;
|
|
break;
|
|
default:
|
|
level = LOG_DEBUG;
|
|
break;
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
/* Format messages as per RFC3164. */
|
|
|
|
/* It appears that some syslog daemon implementations do not allow a
|
|
* hostname when messages are sent via a Unix domain socket, so omit
|
|
* it. Similarly, syslogd on FreeBSD does not understand the hostname
|
|
* part of the header, even when logging via UDP. Note that most
|
|
* implementations will log messages against "localhost" when logging
|
|
* via UDP. A timestamp could be sent but rsyslogd on Linux limits
|
|
* the timestamp logged to the precision that was received on
|
|
* /dev/log. It seems sane to send degenerate RFC3164 messages
|
|
* without a header at all, so that the daemon will generate high
|
|
* resolution timestamps if configured. */
|
|
static int format_rfc3164(int dbglevel, struct ctdb_syslog_sock_state *state,
|
|
const char *str, char *buf, int bsize)
|
|
{
|
|
int pri;
|
|
int len;
|
|
|
|
pri = CTDB_SYSLOG_FACILITY | ctdb_debug_to_syslog_level(dbglevel);
|
|
len = snprintf(buf, bsize, "<%d>%s[%u]: %s%s",
|
|
pri, state->app_name, getpid(), debug_extra, str);
|
|
len = MIN(len, bsize - 1);
|
|
|
|
return len;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
/* Non-blocking logging */
|
|
|
|
static void ctdb_log_to_syslog_sock(void *private_ptr,
|
|
int dbglevel, const char *str)
|
|
{
|
|
struct ctdb_syslog_sock_state *state = talloc_get_type(
|
|
private_ptr, struct ctdb_syslog_sock_state);
|
|
|
|
/* RFC3164 says: The total length of the packet MUST be 1024
|
|
bytes or less. */
|
|
char buf[1024];
|
|
int n;
|
|
|
|
n = format_rfc3164(dbglevel, state, str, buf, sizeof(buf));
|
|
if (n == -1) {
|
|
fprintf(stderr, "Failed to format syslog message %s\n", str);
|
|
return;
|
|
}
|
|
|
|
/* Could extend this to count failures, which probably
|
|
* indicate dropped messages due to EAGAIN or EWOULDBLOCK */
|
|
(void)send(state->fd, buf, n, 0);
|
|
}
|
|
|
|
static int
|
|
ctdb_syslog_sock_state_destructor(struct ctdb_syslog_sock_state *state)
|
|
{
|
|
if (state->fd != -1) {
|
|
close(state->fd);
|
|
state->fd = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct ctdb_syslog_sock_state *
|
|
ctdb_log_setup_syslog_common(TALLOC_CTX *mem_ctx,
|
|
const char *app_name)
|
|
{
|
|
struct ctdb_syslog_sock_state *state;
|
|
|
|
state = talloc_zero(mem_ctx, struct ctdb_syslog_sock_state);
|
|
if (state == NULL) {
|
|
return NULL;
|
|
}
|
|
state->fd = -1;
|
|
state->app_name = app_name;
|
|
talloc_set_destructor(state, ctdb_syslog_sock_state_destructor);
|
|
|
|
return state;
|
|
}
|
|
|
|
static int ctdb_log_setup_syslog_un(TALLOC_CTX *mem_ctx,
|
|
const char *app_name)
|
|
{
|
|
struct ctdb_syslog_sock_state *state;
|
|
struct sockaddr_un dest;
|
|
int ret;
|
|
|
|
state = ctdb_log_setup_syslog_common(mem_ctx, app_name);
|
|
if (state == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
state->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
if (state->fd == -1) {
|
|
int save_errno = errno;
|
|
talloc_free(state);
|
|
return save_errno;
|
|
}
|
|
|
|
dest.sun_family = AF_UNIX;
|
|
strncpy(dest.sun_path, _PATH_LOG, sizeof(dest.sun_path)-1);
|
|
ret = connect(state->fd,
|
|
(struct sockaddr *)&dest, sizeof(dest));
|
|
if (ret == -1) {
|
|
int save_errno = errno;
|
|
talloc_free(state);
|
|
return save_errno;
|
|
}
|
|
set_blocking(state->fd, false);
|
|
|
|
debug_set_callback(state, ctdb_log_to_syslog_sock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ctdb_log_setup_syslog_udp(TALLOC_CTX *mem_ctx,
|
|
const char *app_name)
|
|
{
|
|
struct ctdb_syslog_sock_state *state;
|
|
struct sockaddr_in dest;
|
|
int ret;
|
|
|
|
state = ctdb_log_setup_syslog_common(mem_ctx, app_name);
|
|
if (state == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
state->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (state->fd == -1) {
|
|
int save_errno = errno;
|
|
talloc_free(state);
|
|
return save_errno;
|
|
}
|
|
|
|
dest.sin_family = AF_INET;
|
|
dest.sin_port = htons(514);
|
|
dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
ret = connect(state->fd,
|
|
(struct sockaddr *)&dest, sizeof(dest));
|
|
if (ret == -1) {
|
|
int save_errno = errno;
|
|
talloc_free(state);
|
|
return save_errno;
|
|
}
|
|
|
|
debug_set_callback(state, ctdb_log_to_syslog_sock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
static void ctdb_log_to_syslog(void *private_ptr, int dbglevel, const char *s)
|
|
{
|
|
syslog(ctdb_debug_to_syslog_level(dbglevel),
|
|
"%s%s", debug_extra, s);
|
|
}
|
|
|
|
static int ctdb_log_setup_syslog(TALLOC_CTX *mem_ctx,
|
|
const char *logging,
|
|
const char *app_name)
|
|
{
|
|
size_t l = strlen(CTDB_LOG_SYSLOG_PREFIX);
|
|
|
|
if (logging[l] != '\0') {
|
|
/* Handle non-blocking extensions here */
|
|
const char *method;
|
|
|
|
if (logging[l] != ':') {
|
|
return EINVAL;
|
|
}
|
|
method = &logging[0] + l + 1;
|
|
if (strcmp(method, "nonblocking") == 0) {
|
|
ctdb_log_setup_syslog_un(mem_ctx, app_name);
|
|
return 0;
|
|
}
|
|
if (strcmp(method, "udp") == 0) {
|
|
ctdb_log_setup_syslog_udp(mem_ctx, app_name);
|
|
return 0;
|
|
}
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
debug_set_callback(NULL, ctdb_log_to_syslog);
|
|
return 0;
|
|
}
|
|
|
|
void ctdb_log_init_syslog(void)
|
|
{
|
|
ctdb_log_register_backend(CTDB_LOG_SYSLOG_PREFIX,
|
|
ctdb_log_setup_syslog);
|
|
}
|