blob: ab472120ad2b75015eb15703142608a0d8bf2a09 [file] [log] [blame]
Simon Kelleyf2621c72007-04-29 19:47:21 +01001/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13#include "dnsmasq.h"
14
15/* Implement logging to /dev/log asynchronously. If syslogd is
16 making DNS lookups through dnsmasq, and dnsmasq blocks awaiting
17 syslogd, then the two daemons can deadlock. We get around this
18 by not blocking when talking to syslog, instead we queue up to
19 MAX_LOGS messages. If more are queued, they will be dropped,
20 and the drop event itself logged. */
21
22/* The "wire" protocol for logging is defined in RFC 3164 */
23
24/* From RFC 3164 */
25#define MAX_MESSAGE 1024
26
27/* defaults in case we die() before we log_start() */
28static int log_fac = LOG_DAEMON;
29static int log_stderr = 0;
30static int log_fd = -1;
31static int log_to_file = 0;
32static int entries_alloced = 0;
33static int entries_lost = 0;
34static int connection_good = 1;
35static int max_logs = 0;
36
37struct log_entry {
38 int offset, length;
39 struct log_entry *next;
40 char payload[MAX_MESSAGE];
41};
42
43static struct log_entry *entries = NULL;
44static struct log_entry *free_entries = NULL;
45
46
47int log_start(struct daemon *daemon)
48{
49 int flags;
50
51 log_stderr = !!(daemon->options & OPT_DEBUG);
52
53 if (daemon->log_fac != -1)
54 log_fac = daemon->log_fac;
55#ifdef LOG_LOCAL0
56 else if (daemon->options & OPT_DEBUG)
57 log_fac = LOG_LOCAL0;
58#endif
59
60 if (daemon->log_file)
61 {
62 log_fd = open(daemon->log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
63 log_to_file = 1;
64 daemon->max_logs = 0;
65 }
66 else
67 log_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
68
69 if (log_fd == -1)
70 die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log");
71
72 /* if queuing is inhibited, make sure we allocate
73 the one required buffer now. */
74 if ((max_logs = daemon->max_logs) == 0)
75 {
76 free_entries = safe_malloc(sizeof(struct log_entry));
77 free_entries->next = NULL;
78 entries_alloced = 1;
79 }
80
81 if ((flags = fcntl(log_fd, F_GETFD)) != -1)
82 fcntl(log_fd, F_SETFD, flags | FD_CLOEXEC);
83
84 /* if max_log is zero, leave the socket blocking */
85 if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
86 fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
87
88 return log_fd;
89}
90
91static void log_write(void)
92{
93 ssize_t rc;
94 int tried_stream = 0;
95
96 while (entries)
97 {
98 connection_good = 1;
99
100 if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
101 {
102 entries->length -= rc;
103 entries->offset += rc;
104 if (entries->length == 0)
105 {
106 struct log_entry *tmp = entries;
107 entries = tmp->next;
108 tmp->next = free_entries;
109 free_entries = tmp;
110
111 if (entries_lost != 0)
112 {
113 int e = entries_lost;
114 entries_lost = 0; /* avoid wild recursion */
115 my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e);
116 }
117 }
118 continue;
119 }
120
121 if (errno == EINTR)
122 continue;
123
124 if (errno == EAGAIN)
125 return; /* syslogd busy, go again when select() or poll() says so */
126
127 if (errno == ENOBUFS)
128 {
129 connection_good = 0;
130 return;
131 }
132
133 /* Once a stream socket hits EPIPE, we have to close and re-open */
134 if (errno == EPIPE)
135 goto reopen_stream;
136
137 if (!log_to_file &&
138 (errno == ECONNREFUSED ||
139 errno == ENOTCONN ||
140 errno == EDESTADDRREQ ||
141 errno == ECONNRESET))
142 {
143 /* socket went (syslogd down?), try and reconnect. If we fail,
144 stop trying until the next call to my_syslog()
145 ECONNREFUSED -> connection went down
146 ENOTCONN -> nobody listening
147 (ECONNRESET, EDESTADDRREQ are *BSD equivalents)
148 EPIPE comes from broken stream socket (we ignore SIGPIPE) */
149
150 struct sockaddr_un logaddr;
151
152#ifdef HAVE_SOCKADDR_SA_LEN
153 logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1;
154#endif
155 logaddr.sun_family = AF_LOCAL;
156 strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
157
158 /* Got connection back? try again. */
159 if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
160 continue;
161
162 /* errors from connect which mean we should keep trying */
163 if (errno == ENOENT ||
164 errno == EALREADY ||
165 errno == ECONNREFUSED ||
166 errno == EISCONN ||
167 errno == EINTR ||
168 errno == EAGAIN)
169 {
170 /* try again on next syslog() call */
171 connection_good = 0;
172 return;
173 }
174
175 /* we start with a SOCK_DGRAM socket, but syslog may want SOCK_STREAM */
176 if (!tried_stream && errno == EPROTOTYPE)
177 {
178 reopen_stream:
179 tried_stream = 1;
180 close(log_fd);
181 if ((log_fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1)
182 {
183 int flags;
184
185 if ((flags = fcntl(log_fd, F_GETFD)) != -1)
186 fcntl(log_fd, F_SETFD, flags | FD_CLOEXEC);
187
188 /* if max_log is zero, leave the socket blocking */
189 if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
190 fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
191
192 continue;
193 }
194 }
195 }
196
197 /* give up - fall back to syslog() - this handles out-of-space
198 when logging to a file, for instance. */
199 log_fd = -1;
200 my_syslog(LOG_CRIT, _("log failed: %s"), strerror(errno));
201 return;
202 }
203}
204
205void my_syslog(int priority, const char *format, ...)
206{
207 va_list ap;
208 struct log_entry *entry;
209 time_t time_now;
210 char *p;
211 size_t len;
212
213 va_start(ap, format);
214
215 if (log_stderr)
216 {
217 fprintf(stderr, "dnsmasq: ");
218 vfprintf(stderr, format, ap);
219 fputc('\n', stderr);
220 }
221
222 if (log_fd == -1)
223 {
224 /* fall-back to syslog if we die during startup or fail during running. */
225 static int isopen = 0;
226 if (!isopen)
227 {
228 openlog("dnsmasq", LOG_PID, log_fac);
229 isopen = 1;
230 }
231 vsyslog(priority, format, ap);
232 va_end(ap);
233 return;
234 }
235
236 if ((entry = free_entries))
237 free_entries = entry->next;
238 else if (entries_alloced < max_logs && (entry = malloc(sizeof(struct log_entry))))
239 entries_alloced++;
240
241 if (!entry)
242 entries_lost++;
243 else
244 {
245 /* add to end of list, consumed from the start */
246 entry->next = NULL;
247 if (!entries)
248 entries = entry;
249 else
250 {
251 struct log_entry *tmp;
252 for (tmp = entries; tmp->next; tmp = tmp->next);
253 tmp->next = entry;
254 }
255
256 time(&time_now);
257 p = entry->payload;
258 if (!log_to_file)
259 p += sprintf(p, "<%d>", priority | log_fac);
260
261 p += sprintf(p, "%.15s dnsmasq[%d]: ", ctime(&time_now) + 4, getpid());
262 len = p - entry->payload;
263 len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
264 entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
265 entry->offset = 0;
266
267 /* replace terminator with \n */
268 if (log_to_file)
269 entry->payload[entry->length - 1] = '\n';
270 }
271
272 /* almost always, logging won't block, so try and write this now,
273 to save collecting too many log messages during a select loop. */
274 log_write();
275
276 /* Since we're doing things asynchronously, a cache-dump, for instance,
277 can now generate log lines very fast. With a small buffer (desirable),
278 that means it can overflow the log-buffer very quickly,
279 so that the cache dump becomes mainly a count of how many lines
280 overflowed. To avoid this, we delay here, the delay is controlled
281 by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms.
282 The scaling stuff ensures that when the queue is bigger than 8, the delay
283 only occurs for the last 8 entries. Once the queue is full, we stop delaying
284 to preserve performance.
285 */
286
287 if (entries && max_logs != 0)
288 {
289 int d;
290
291 for (d = 0,entry = entries; entry; entry = entry->next, d++);
292
293 if (d == max_logs)
294 d = 0;
295 else if (max_logs > 8)
296 d -= max_logs - 8;
297
298 if (d > 0)
299 {
300 struct timespec waiter;
301 waiter.tv_sec = 0;
302 waiter.tv_nsec = 1000000 << (d - 1); /* 1 ms */
303 nanosleep(&waiter, NULL);
304
305 /* Have another go now */
306 log_write();
307 }
308 }
309
310 va_end(ap);
311}
312
313void set_log_writer(fd_set *set, int *maxfdp)
314{
315 if (entries && log_fd != -1 && connection_good)
316 {
317 FD_SET(log_fd, set);
318 bump_maxfd(log_fd, maxfdp);
319 }
320}
321
322void check_log_writer(fd_set *set)
323{
324 if (log_fd != -1 && FD_ISSET(log_fd, set))
325 log_write();
326}
327
328void die(char *message, char *arg1)
329{
330 char *errmess = strerror(errno);
331
332 if (!arg1)
333 arg1 = errmess;
334
335 log_stderr = 1; /* print as well as log when we die.... */
336 my_syslog(LOG_CRIT, message, arg1, errmess);
337
338 log_stderr = 0;
339 my_syslog(LOG_CRIT, _("FAILED to start up"));
340
341 exit(1);
342}