6a3fd02c1622a1fd7bc8dedab71ee220586b9790
[kamenim/samba-autobuild/.git] / ctdb / server / ctdb_logging_syslog.c
1 /*
2    ctdb logging code - syslog backend
3
4    Copyright (C) Andrew Tridgell  2008
5    Copyright (C) Martin Schwenke  2014
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "replace.h"
22 #include "system/network.h"
23 #include "system/syslog.h"
24 #include "lib/util/debug.h"
25 #include "lib/util/blocking.h"
26 #include "ctdb_logging.h"
27
28 /* Linux and FreeBSD define this appropriately - try good old /dev/log
29  * for anything that doesn't... */
30 #ifndef _PATH_LOG
31 #define _PATH_LOG "/dev/log"
32 #endif
33
34 #define CTDB_LOG_SYSLOG_PREFIX "syslog"
35 #define CTDB_SYSLOG_FACILITY LOG_USER
36
37 struct ctdb_syslog_sock_state {
38         int fd;
39         const char *app_name;
40 };
41
42 /**********************************************************************/
43
44 static int ctdb_debug_to_syslog_level(int dbglevel)
45 {
46         int level;
47
48         switch (dbglevel) {
49         case DEBUG_ERR:
50                 level = LOG_ERR;
51                 break;
52         case DEBUG_WARNING:
53                 level = LOG_WARNING;
54                 break;
55         case DEBUG_NOTICE:
56                 level = LOG_NOTICE;
57                 break;
58         case DEBUG_INFO:
59                 level = LOG_INFO;
60                 break;
61         default:
62                 level = LOG_DEBUG;
63                 break;
64         }
65
66         return level;
67 }
68
69 /**********************************************************************/
70
71 /* Format messages as per RFC3164. */
72
73 /* It appears that some syslog daemon implementations do not allow a
74  * hostname when messages are sent via a Unix domain socket, so omit
75  * it.  A timestamp could be sent but rsyslogd on Linux limits the
76  * timestamp logged to the precision that was received on /dev/log.
77  * It seems sane to send degenerate RFC3164 messages without a header
78  * at all, so that the daemon will generate high resolution timestamps
79  * if configured. */
80 static int format_rfc3164(int dbglevel, struct ctdb_syslog_sock_state *state,
81                           const char *str, char *buf, int bsize)
82 {
83         int pri;
84         int len;
85
86         pri = CTDB_SYSLOG_FACILITY | ctdb_debug_to_syslog_level(dbglevel);
87         len = snprintf(buf, bsize, "<%d>%s[%u]: %s%s",
88                        pri, state->app_name, getpid(), debug_extra, str);
89         len = MIN(len, bsize - 1);
90
91         return len;
92 }
93
94 /**********************************************************************/
95
96 /* Non-blocking logging */
97
98 static void ctdb_log_to_syslog_sock(void *private_ptr,
99                                     int dbglevel, const char *str)
100 {
101         struct ctdb_syslog_sock_state *state = talloc_get_type(
102                 private_ptr, struct ctdb_syslog_sock_state);
103
104         /* RFC3164 says: The total length of the packet MUST be 1024
105            bytes or less. */
106         char buf[1024];
107         int n;
108
109         n = format_rfc3164(dbglevel, state, str, buf, sizeof(buf));
110         if (n == -1) {
111                 fprintf(stderr, "Failed to format syslog message %s\n", str);
112                 return;
113         }
114
115         /* Could extend this to count failures, which probably
116          * indicate dropped messages due to EAGAIN or EWOULDBLOCK */
117         (void)send(state->fd, buf, n, 0);
118 }
119
120 static int
121 ctdb_syslog_sock_state_destructor(struct ctdb_syslog_sock_state *state)
122 {
123         if (state->fd != -1) {
124                 close(state->fd);
125                 state->fd = -1;
126         }
127         return 0;
128 }
129
130 static int ctdb_log_setup_syslog_un(TALLOC_CTX *mem_ctx,
131                                     const char *app_name)
132 {
133         struct ctdb_syslog_sock_state *state;
134         struct sockaddr_un dest;
135         int ret;
136
137         state = talloc_zero(mem_ctx, struct ctdb_syslog_sock_state);
138         if (state == NULL) {
139                 return ENOMEM;
140         }
141
142         state->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
143         if (state->fd == -1) {
144                 int save_errno = errno;
145                 talloc_free(state);
146                 return save_errno;
147         }
148
149         dest.sun_family = AF_UNIX;
150         strncpy(dest.sun_path, _PATH_LOG, sizeof(dest.sun_path)-1);
151         ret = connect(state->fd,
152                       (struct sockaddr *)&dest, sizeof(dest));
153         if (ret == -1) {
154                 int save_errno = errno;
155                 talloc_free(state);
156                 return save_errno;
157         }
158         set_blocking(state->fd, false);
159
160         state->app_name = app_name;
161
162         talloc_set_destructor(state, ctdb_syslog_sock_state_destructor);
163         debug_set_callback(state, ctdb_log_to_syslog_sock);
164
165         return 0;
166 }
167
168 /**********************************************************************/
169
170 static void ctdb_log_to_syslog(void *private_ptr, int dbglevel, const char *s)
171 {
172         syslog(ctdb_debug_to_syslog_level(dbglevel),
173                "%s%s", debug_extra, s);
174 }
175
176 static int ctdb_log_setup_syslog(TALLOC_CTX *mem_ctx,
177                                  const char *logging,
178                                  const char *app_name)
179 {
180         size_t l = strlen(CTDB_LOG_SYSLOG_PREFIX);
181
182         if (logging[l] != '\0') {
183                 /* Handle non-blocking extensions here */
184                 const char *method;
185
186                 if (logging[l] != ':') {
187                         return EINVAL;
188                 }
189                 method = &logging[0] + l + 1;
190                 if (strcmp(method, "nonblocking") == 0) {
191                         ctdb_log_setup_syslog_un(mem_ctx, app_name);
192                         return 0;
193                 }
194
195                 return EINVAL;
196         }
197
198         debug_set_callback(NULL, ctdb_log_to_syslog);
199         return 0;
200 }
201
202 void ctdb_log_init_syslog(void)
203 {
204         ctdb_log_register_backend(CTDB_LOG_SYSLOG_PREFIX,
205                                   ctdb_log_setup_syslog);
206 }