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