1 /* dnsmasq is Copyright (c) 2000-2009 Simon Kelley
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.
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.
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/>.
19 /* Implement logging to /dev/log asynchronously. If syslogd is
20 making DNS lookups through dnsmasq, and dnsmasq blocks awaiting
21 syslogd, then the two daemons can deadlock. We get around this
22 by not blocking when talking to syslog, instead we queue up to
23 MAX_LOGS messages. If more are queued, they will be dropped,
24 and the drop event itself logged. */
26 /* The "wire" protocol for logging is defined in RFC 3164 */
29 #define MAX_MESSAGE 1024
31 /* defaults in case we die() before we log_start() */
32 static int log_fac
= LOG_DAEMON
;
33 static int log_stderr
= 0;
34 static int log_fd
= -1;
35 static int log_to_file
= 0;
36 static int entries_alloced
= 0;
37 static int entries_lost
= 0;
38 static int connection_good
= 1;
39 static int max_logs
= 0;
40 static int connection_type
= SOCK_DGRAM
;
44 pid_t pid
; /* to avoid duplicates over a fork */
45 struct log_entry
*next
;
46 char payload
[MAX_MESSAGE
];
49 static struct log_entry
*entries
= NULL
;
50 static struct log_entry
*free_entries
= NULL
;
53 int log_start(struct passwd
*ent_pw
, int errfd
)
57 log_stderr
= !!(daemon
->options
& OPT_DEBUG
);
59 if (daemon
->log_fac
!= -1)
60 log_fac
= daemon
->log_fac
;
62 else if (daemon
->options
& OPT_DEBUG
)
72 max_logs
= daemon
->max_logs
;
74 if (!log_reopen(daemon
->log_file
))
76 send_event(errfd
, EVENT_LOG_ERR
, errno
);
80 /* if queuing is inhibited, make sure we allocate
81 the one required buffer now. */
84 free_entries
= safe_malloc(sizeof(struct log_entry
));
85 free_entries
->next
= NULL
;
89 /* If we're running as root and going to change uid later,
90 change the ownership here so that the file is always owned by
91 the dnsmasq user. Then logrotate can just copy the owner.
92 Failure of the chown call is OK, (for instance when started as non-root) */
93 if (log_to_file
&& ent_pw
&& ent_pw
->pw_uid
!= 0 &&
94 fchown(log_fd
, ent_pw
->pw_uid
, -1) != 0)
100 int log_reopen(char *log_file
)
105 /* NOTE: umask is set to 022 by the time this gets called */
109 log_fd
= open(log_file
, O_WRONLY
|O_CREAT
|O_APPEND
, S_IRUSR
|S_IWUSR
|S_IRGRP
);
113 #ifdef HAVE_SOLARIS_NETWORK
114 /* Solaris logging is "different", /dev/log is not unix-domain socket.
115 Just leave log_fd == -1 and use the vsyslog call for everything.... */
116 # define _PATH_LOG "" /* dummy */
121 log_fd
= socket(AF_UNIX
, connection_type
, 0);
126 /* if max_logs is zero, leave the socket blocking */
127 if (max_logs
!= 0 && (flags
= fcntl(log_fd
, F_GETFL
)) != -1)
128 fcntl(log_fd
, F_SETFL
, flags
| O_NONBLOCK
);
135 static void free_entry(void)
137 struct log_entry
*tmp
= entries
;
139 tmp
->next
= free_entries
;
143 static void log_write(void)
149 /* Avoid duplicates over a fork() */
150 if (entries
->pid
!= getpid())
158 if ((rc
= write(log_fd
, entries
->payload
+ entries
->offset
, entries
->length
)) != -1)
160 entries
->length
-= rc
;
161 entries
->offset
+= rc
;
162 if (entries
->length
== 0)
165 if (entries_lost
!= 0)
167 int e
= entries_lost
;
168 entries_lost
= 0; /* avoid wild recursion */
169 my_syslog(LOG_WARNING
, _("overflow: %d log entries lost"), e
);
179 return; /* syslogd busy, go again when select() or poll() says so */
181 if (errno
== ENOBUFS
)
187 /* errors handling after this assumes sockets */
190 /* Once a stream socket hits EPIPE, we have to close and re-open
191 (we ignore SIGPIPE) */
194 if (log_reopen(NULL
))
197 else if (errno
== ECONNREFUSED
||
199 errno
== EDESTADDRREQ
||
202 /* socket went (syslogd down?), try and reconnect. If we fail,
203 stop trying until the next call to my_syslog()
204 ECONNREFUSED -> connection went down
205 ENOTCONN -> nobody listening
206 (ECONNRESET, EDESTADDRREQ are *BSD equivalents) */
208 struct sockaddr_un logaddr
;
210 #ifdef HAVE_SOCKADDR_SA_LEN
211 logaddr
.sun_len
= sizeof(logaddr
) - sizeof(logaddr
.sun_path
) + strlen(_PATH_LOG
) + 1;
213 logaddr
.sun_family
= AF_UNIX
;
214 strncpy(logaddr
.sun_path
, _PATH_LOG
, sizeof(logaddr
.sun_path
));
216 /* Got connection back? try again. */
217 if (connect(log_fd
, (struct sockaddr
*)&logaddr
, sizeof(logaddr
)) != -1)
220 /* errors from connect which mean we should keep trying */
221 if (errno
== ENOENT
||
223 errno
== ECONNREFUSED
||
228 /* try again on next syslog() call */
233 /* try the other sort of socket... */
234 if (errno
== EPROTOTYPE
)
236 connection_type
= connection_type
== SOCK_DGRAM
? SOCK_STREAM
: SOCK_DGRAM
;
237 if (log_reopen(NULL
))
243 /* give up - fall back to syslog() - this handles out-of-space
244 when logging to a file, for instance. */
246 my_syslog(LOG_CRIT
, _("log failed: %s"), strerror(errno
));
251 /* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h.
252 OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
253 DNS, DHCP and TFTP services.
255 void my_syslog(int priority
, const char *format
, ...)
258 struct log_entry
*entry
;
262 pid_t pid
= getpid();
265 if ((LOG_FACMASK
& priority
) == MS_TFTP
)
267 else if ((LOG_FACMASK
& priority
) == MS_DHCP
)
270 priority
= LOG_PRI(priority
);
274 fprintf(stderr
, "dnsmasq%s: ", func
);
275 va_start(ap
, format
);
276 vfprintf(stderr
, format
, ap
);
283 /* fall-back to syslog if we die during startup or fail during running. */
284 static int isopen
= 0;
287 openlog("dnsmasq", LOG_PID
, log_fac
);
290 va_start(ap
, format
);
291 vsyslog(priority
, format
, ap
);
296 if ((entry
= free_entries
))
297 free_entries
= entry
->next
;
298 else if (entries_alloced
< max_logs
&& (entry
= malloc(sizeof(struct log_entry
))))
305 /* add to end of list, consumed from the start */
311 struct log_entry
*tmp
;
312 for (tmp
= entries
; tmp
->next
; tmp
= tmp
->next
);
319 p
+= sprintf(p
, "<%d>", priority
| log_fac
);
321 p
+= sprintf(p
, "%.15s dnsmasq%s[%d]: ", ctime(&time_now
) + 4, func
, (int)pid
);
323 len
= p
- entry
->payload
;
324 va_start(ap
, format
);
325 len
+= vsnprintf(p
, MAX_MESSAGE
- len
, format
, ap
) + 1; /* include zero-terminator */
327 entry
->length
= len
> MAX_MESSAGE
? MAX_MESSAGE
: len
;
331 /* replace terminator with \n */
333 entry
->payload
[entry
->length
- 1] = '\n';
336 /* almost always, logging won't block, so try and write this now,
337 to save collecting too many log messages during a select loop. */
340 /* Since we're doing things asynchronously, a cache-dump, for instance,
341 can now generate log lines very fast. With a small buffer (desirable),
342 that means it can overflow the log-buffer very quickly,
343 so that the cache dump becomes mainly a count of how many lines
344 overflowed. To avoid this, we delay here, the delay is controlled
345 by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms.
346 The scaling stuff ensures that when the queue is bigger than 8, the delay
347 only occurs for the last 8 entries. Once the queue is full, we stop delaying
348 to preserve performance.
351 if (entries
&& max_logs
!= 0)
355 for (d
= 0,entry
= entries
; entry
; entry
= entry
->next
, d
++);
359 else if (max_logs
> 8)
364 struct timespec waiter
;
366 waiter
.tv_nsec
= 1000000 << (d
- 1); /* 1 ms */
367 nanosleep(&waiter
, NULL
);
369 /* Have another go now */
375 void set_log_writer(fd_set
*set
, int *maxfdp
)
377 if (entries
&& log_fd
!= -1 && connection_good
)
380 bump_maxfd(log_fd
, maxfdp
);
384 void check_log_writer(fd_set
*set
)
386 if (log_fd
!= -1 && (!set
|| FD_ISSET(log_fd
, set
)))
392 /* block until queue empty */
396 if ((flags
= fcntl(log_fd
, F_GETFL
)) != -1)
397 fcntl(log_fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
403 void die(char *message
, char *arg1
, int exit_code
)
405 char *errmess
= strerror(errno
);
410 log_stderr
= 1; /* print as well as log when we die.... */
411 fputc('\n', stderr
); /* prettyfy startup-script message */
412 my_syslog(LOG_CRIT
, message
, arg1
, errmess
);
415 my_syslog(LOG_CRIT
, _("FAILED to start up"));