Line | Count | Source |
1 | | /* dnsmasq is Copyright (c) 2000-2025 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 | 0 | #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 echo_stderr = 0; |
39 | | static int log_fd = -1; |
40 | | static int log_to_file = 0; |
41 | | static int entries_alloced = 0; |
42 | | static int entries_lost = 0; |
43 | | static int connection_good = 1; |
44 | | static int max_logs = 0; |
45 | | static int connection_type = SOCK_DGRAM; |
46 | | |
47 | | struct log_entry { |
48 | | int offset, length; |
49 | | pid_t pid; /* to avoid duplicates over a fork */ |
50 | | struct log_entry *next; |
51 | | char payload[MAX_MESSAGE]; |
52 | | }; |
53 | | |
54 | | static struct log_entry *entries = NULL; |
55 | | static struct log_entry *free_entries = NULL; |
56 | | |
57 | | |
58 | | int log_start(struct passwd *ent_pw, int errfd) |
59 | 0 | { |
60 | 0 | int ret = 0; |
61 | |
|
62 | 0 | echo_stderr = option_bool(OPT_DEBUG); |
63 | |
|
64 | 0 | if (daemon->log_fac != -1) |
65 | 0 | log_fac = daemon->log_fac; |
66 | 0 | #ifdef LOG_LOCAL0 |
67 | 0 | else if (option_bool(OPT_DEBUG)) |
68 | 0 | log_fac = LOG_LOCAL0; |
69 | 0 | #endif |
70 | |
|
71 | 0 | if (daemon->log_file) |
72 | 0 | { |
73 | 0 | log_to_file = 1; |
74 | 0 | daemon->max_logs = 0; |
75 | 0 | if (strcmp(daemon->log_file, "-") == 0) |
76 | 0 | { |
77 | 0 | log_stderr = 1; |
78 | 0 | echo_stderr = 0; |
79 | 0 | log_fd = dup(STDERR_FILENO); |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | 0 | max_logs = daemon->max_logs; |
84 | |
|
85 | 0 | if (!log_reopen(daemon->log_file)) |
86 | 0 | { |
87 | 0 | send_event(errfd, EVENT_LOG_ERR, errno, daemon->log_file ? daemon->log_file : ""); |
88 | 0 | _exit(0); |
89 | 0 | } |
90 | | |
91 | | /* if queuing is inhibited, make sure we allocate |
92 | | the one required buffer now. */ |
93 | 0 | if (max_logs == 0) |
94 | 0 | { |
95 | 0 | free_entries = safe_malloc(sizeof(struct log_entry)); |
96 | 0 | free_entries->next = NULL; |
97 | 0 | entries_alloced = 1; |
98 | 0 | } |
99 | | |
100 | | /* If we're running as root and going to change uid later, |
101 | | change the ownership here so that the file is always owned by |
102 | | the dnsmasq user. Then logrotate can just copy the owner. |
103 | | Failure of the chown call is OK, (for instance when started as non-root). |
104 | | |
105 | | If we've created a file with group-id root, we also make |
106 | | the file group-writable. This gives processes in the root group |
107 | | write access to the file and avoids the problem that on some systems, |
108 | | once the file is owned by the dnsmasq user, it can't be written |
109 | | whilst dnsmasq is running as root during startup. |
110 | | */ |
111 | 0 | if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0) |
112 | 0 | { |
113 | 0 | struct stat ls; |
114 | 0 | if (getgid() == 0 && fstat(log_fd, &ls) == 0 && ls.st_gid == 0 && |
115 | 0 | (ls.st_mode & S_IWGRP) == 0) |
116 | 0 | (void)fchmod(log_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); |
117 | 0 | if (fchown(log_fd, ent_pw->pw_uid, -1) != 0) |
118 | 0 | ret = errno; |
119 | 0 | } |
120 | |
|
121 | 0 | return ret; |
122 | 0 | } |
123 | | |
124 | | int log_reopen(char *log_file) |
125 | 0 | { |
126 | 0 | if (!log_stderr) |
127 | 0 | { |
128 | 0 | if (log_fd != -1) |
129 | 0 | close(log_fd); |
130 | | |
131 | | /* NOTE: umask is set to 022 by the time this gets called */ |
132 | | |
133 | 0 | if (log_file) |
134 | 0 | log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP); |
135 | 0 | else |
136 | 0 | { |
137 | | #if defined(HAVE_SOLARIS_NETWORK) || defined(__ANDROID__) |
138 | | /* Solaris logging is "different", /dev/log is not unix-domain socket. |
139 | | Just leave log_fd == -1 and use the vsyslog call for everything.... */ |
140 | | # define _PATH_LOG "" /* dummy */ |
141 | | return 1; |
142 | | #else |
143 | 0 | int flags; |
144 | 0 | log_fd = socket(AF_UNIX, connection_type, 0); |
145 | | |
146 | | /* if max_logs is zero, leave the socket blocking */ |
147 | 0 | if (log_fd != -1 && max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1) |
148 | 0 | fcntl(log_fd, F_SETFL, flags | O_NONBLOCK); |
149 | 0 | #endif |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | 0 | return log_fd != -1; |
154 | 0 | } |
155 | | |
156 | | static void free_entry(void) |
157 | 0 | { |
158 | 0 | struct log_entry *tmp = entries; |
159 | 0 | entries = tmp->next; |
160 | 0 | tmp->next = free_entries; |
161 | 0 | free_entries = tmp; |
162 | 0 | } |
163 | | |
164 | | static void log_write(void) |
165 | 0 | { |
166 | 0 | ssize_t rc; |
167 | | |
168 | 0 | while (entries) |
169 | 0 | { |
170 | | /* The data in the payload is written with a terminating zero character |
171 | | and the length reflects this. For a stream connection we need to |
172 | | send the zero as a record terminator, but this isn't done for a |
173 | | datagram connection, so treat the length as one less than reality |
174 | | to elide the zero. If we're logging to a file, turn the zero into |
175 | | a newline, and leave the length alone. */ |
176 | 0 | int len_adjust = 0; |
177 | |
|
178 | 0 | if (log_to_file) |
179 | 0 | entries->payload[entries->offset + entries->length - 1] = '\n'; |
180 | 0 | else if (connection_type == SOCK_DGRAM) |
181 | 0 | len_adjust = 1; |
182 | | |
183 | | /* Avoid duplicates over a fork() */ |
184 | 0 | if (entries->pid != getpid()) |
185 | 0 | { |
186 | 0 | free_entry(); |
187 | 0 | continue; |
188 | 0 | } |
189 | | |
190 | 0 | connection_good = 1; |
191 | |
|
192 | 0 | if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1) |
193 | 0 | { |
194 | 0 | entries->length -= rc; |
195 | 0 | entries->offset += rc; |
196 | 0 | if (entries->length == len_adjust) |
197 | 0 | { |
198 | 0 | free_entry(); |
199 | 0 | if (entries_lost != 0) |
200 | 0 | { |
201 | 0 | int e = entries_lost; |
202 | 0 | entries_lost = 0; /* avoid wild recursion */ |
203 | 0 | my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e); |
204 | 0 | } |
205 | 0 | } |
206 | 0 | continue; |
207 | 0 | } |
208 | | |
209 | 0 | if (errno == EINTR) |
210 | 0 | continue; |
211 | | |
212 | 0 | if (errno == EAGAIN || errno == EWOULDBLOCK) |
213 | 0 | return; /* syslogd busy, go again when select() or poll() says so */ |
214 | | |
215 | 0 | if (errno == ENOBUFS) |
216 | 0 | { |
217 | 0 | connection_good = 0; |
218 | 0 | return; |
219 | 0 | } |
220 | | |
221 | | /* errors handling after this assumes sockets */ |
222 | 0 | if (!log_to_file) |
223 | 0 | { |
224 | | /* Once a stream socket hits EPIPE, we have to close and re-open |
225 | | (we ignore SIGPIPE) */ |
226 | 0 | if (errno == EPIPE) |
227 | 0 | { |
228 | 0 | if (log_reopen(NULL)) |
229 | 0 | continue; |
230 | 0 | } |
231 | 0 | else if (errno == ECONNREFUSED || |
232 | 0 | errno == ENOTCONN || |
233 | 0 | errno == EDESTADDRREQ || |
234 | 0 | errno == ECONNRESET) |
235 | 0 | { |
236 | | /* socket went (syslogd down?), try and reconnect. If we fail, |
237 | | stop trying until the next call to my_syslog() |
238 | | ECONNREFUSED -> connection went down |
239 | | ENOTCONN -> nobody listening |
240 | | (ECONNRESET, EDESTADDRREQ are *BSD equivalents) */ |
241 | | |
242 | 0 | struct sockaddr_un logaddr; |
243 | | |
244 | | #ifdef HAVE_SOCKADDR_SA_LEN |
245 | | logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1; |
246 | | #endif |
247 | 0 | logaddr.sun_family = AF_UNIX; |
248 | 0 | safe_strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path)); |
249 | | |
250 | | /* Got connection back? try again. */ |
251 | 0 | if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1) |
252 | 0 | continue; |
253 | | |
254 | | /* errors from connect which mean we should keep trying */ |
255 | 0 | if (errno == ENOENT || |
256 | 0 | errno == EALREADY || |
257 | 0 | errno == ECONNREFUSED || |
258 | 0 | errno == EISCONN || |
259 | 0 | errno == EINTR || |
260 | 0 | errno == EAGAIN || |
261 | 0 | errno == EWOULDBLOCK) |
262 | 0 | { |
263 | | /* try again on next syslog() call */ |
264 | 0 | connection_good = 0; |
265 | 0 | return; |
266 | 0 | } |
267 | | |
268 | | /* try the other sort of socket... */ |
269 | 0 | if (errno == EPROTOTYPE) |
270 | 0 | { |
271 | 0 | connection_type = connection_type == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM; |
272 | 0 | if (log_reopen(NULL)) |
273 | 0 | continue; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | | /* give up - fall back to syslog() - this handles out-of-space |
279 | | when logging to a file, for instance. */ |
280 | 0 | log_fd = -1; |
281 | 0 | my_syslog(LOG_CRIT, _("log failed: %s"), strerror(errno)); |
282 | 0 | return; |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | /* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h. |
287 | | OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between |
288 | | DNS, DHCP and TFTP services. |
289 | | If OR'd with MS_DEBUG, the messages are suppressed unless --log-debug is set. */ |
290 | | void my_syslog(int priority, const char *format, ...) |
291 | 0 | { |
292 | 0 | va_list ap; |
293 | 0 | struct log_entry *entry; |
294 | 0 | time_t time_now; |
295 | 0 | char *p; |
296 | 0 | size_t len; |
297 | 0 | pid_t pid = getpid(); |
298 | 0 | char *func = ""; |
299 | |
|
300 | 0 | if ((LOG_FACMASK & priority) == MS_TFTP) |
301 | 0 | func = "-tftp"; |
302 | 0 | else if ((LOG_FACMASK & priority) == MS_DHCP) |
303 | 0 | func = "-dhcp"; |
304 | 0 | else if ((LOG_FACMASK & priority) == MS_SCRIPT) |
305 | 0 | func = "-script"; |
306 | 0 | else if ((LOG_FACMASK & priority) == MS_DEBUG) |
307 | 0 | { |
308 | 0 | if (!option_bool(OPT_LOG_DEBUG)) |
309 | 0 | return; |
310 | 0 | func = "-debug"; |
311 | 0 | } |
312 | | |
313 | 0 | #ifdef LOG_PRI |
314 | 0 | priority = LOG_PRI(priority); |
315 | | #else |
316 | | /* Solaris doesn't have LOG_PRI */ |
317 | | priority &= LOG_PRIMASK; |
318 | | #endif |
319 | |
|
320 | 0 | if (echo_stderr) |
321 | 0 | { |
322 | 0 | fprintf(stderr, "dnsmasq%s: ", func); |
323 | 0 | va_start(ap, format); |
324 | 0 | vfprintf(stderr, format, ap); |
325 | 0 | va_end(ap); |
326 | 0 | fputc('\n', stderr); |
327 | 0 | } |
328 | |
|
329 | 0 | if (log_fd == -1) |
330 | 0 | { |
331 | | #ifdef __ANDROID__ |
332 | | /* do android-specific logging. |
333 | | log_fd is always -1 on Android except when logging to a file. */ |
334 | | int alog_lvl; |
335 | | |
336 | | if (priority <= LOG_ERR) |
337 | | alog_lvl = ANDROID_LOG_ERROR; |
338 | | else if (priority == LOG_WARNING) |
339 | | alog_lvl = ANDROID_LOG_WARN; |
340 | | else if (priority <= LOG_INFO) |
341 | | alog_lvl = ANDROID_LOG_INFO; |
342 | | else |
343 | | alog_lvl = ANDROID_LOG_DEBUG; |
344 | | |
345 | | va_start(ap, format); |
346 | | __android_log_vprint(alog_lvl, "dnsmasq", format, ap); |
347 | | va_end(ap); |
348 | | #else |
349 | | /* fall-back to syslog if we die during startup or |
350 | | fail during running (always on Solaris). */ |
351 | 0 | static int isopen = 0; |
352 | |
|
353 | 0 | if (!isopen) |
354 | 0 | { |
355 | 0 | openlog("dnsmasq", LOG_PID, log_fac); |
356 | 0 | isopen = 1; |
357 | 0 | } |
358 | 0 | va_start(ap, format); |
359 | 0 | vsyslog(priority, format, ap); |
360 | 0 | va_end(ap); |
361 | 0 | #endif |
362 | |
|
363 | 0 | return; |
364 | 0 | } |
365 | | |
366 | 0 | if ((entry = free_entries)) |
367 | 0 | free_entries = entry->next; |
368 | 0 | else if (entries_alloced < max_logs && (entry = malloc(sizeof(struct log_entry)))) |
369 | 0 | entries_alloced++; |
370 | | |
371 | 0 | if (!entry) |
372 | 0 | entries_lost++; |
373 | 0 | else |
374 | 0 | { |
375 | | /* add to end of list, consumed from the start */ |
376 | 0 | entry->next = NULL; |
377 | 0 | if (!entries) |
378 | 0 | entries = entry; |
379 | 0 | else |
380 | 0 | { |
381 | 0 | struct log_entry *tmp; |
382 | 0 | for (tmp = entries; tmp->next; tmp = tmp->next); |
383 | 0 | tmp->next = entry; |
384 | 0 | } |
385 | | |
386 | 0 | time(&time_now); |
387 | 0 | p = entry->payload; |
388 | 0 | if (!log_to_file) |
389 | 0 | p += sprintf(p, "<%d>", priority | log_fac); |
390 | | |
391 | | /* Omit timestamp for default daemontools situation */ |
392 | 0 | if (!log_stderr || !option_bool(OPT_NO_FORK)) |
393 | 0 | p += sprintf(p, "%.15s ", ctime(&time_now) + 4); |
394 | | |
395 | 0 | p += sprintf(p, "dnsmasq%s[%d]: ", func, (int)pid); |
396 | | |
397 | 0 | len = p - entry->payload; |
398 | 0 | va_start(ap, format); |
399 | 0 | len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */ |
400 | 0 | va_end(ap); |
401 | 0 | entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len; |
402 | 0 | entry->offset = 0; |
403 | 0 | entry->pid = pid; |
404 | 0 | } |
405 | | |
406 | | /* almost always, logging won't block, so try and write this now, |
407 | | to save collecting too many log messages during a select loop. */ |
408 | 0 | log_write(); |
409 | | |
410 | | /* Since we're doing things asynchronously, a cache-dump, for instance, |
411 | | can now generate log lines very fast. With a small buffer (desirable), |
412 | | that means it can overflow the log-buffer very quickly, |
413 | | so that the cache dump becomes mainly a count of how many lines |
414 | | overflowed. To avoid this, we delay here, the delay is controlled |
415 | | by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms. |
416 | | The scaling stuff ensures that when the queue is bigger than 8, the delay |
417 | | only occurs for the last 8 entries. Once the queue is full, we stop delaying |
418 | | to preserve performance. |
419 | | */ |
420 | |
|
421 | 0 | if (entries && max_logs != 0) |
422 | 0 | { |
423 | 0 | int d; |
424 | | |
425 | 0 | for (d = 0,entry = entries; entry; entry = entry->next, d++); |
426 | | |
427 | 0 | if (d == max_logs) |
428 | 0 | d = 0; |
429 | 0 | else if (max_logs > 8) |
430 | 0 | d -= max_logs - 8; |
431 | |
|
432 | 0 | if (d > 0) |
433 | 0 | { |
434 | 0 | struct timespec waiter; |
435 | 0 | waiter.tv_sec = 0; |
436 | 0 | waiter.tv_nsec = 1000000 << (d - 1); /* 1 ms */ |
437 | 0 | nanosleep(&waiter, NULL); |
438 | | |
439 | | /* Have another go now */ |
440 | 0 | log_write(); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | void set_log_writer(void) |
446 | 0 | { |
447 | 0 | if (entries && log_fd != -1 && connection_good) |
448 | 0 | poll_listen(log_fd, POLLOUT); |
449 | 0 | } |
450 | | |
451 | | void check_log_writer(int force) |
452 | 0 | { |
453 | 0 | if (log_fd != -1 && (force || poll_check(log_fd, POLLOUT))) |
454 | 0 | log_write(); |
455 | 0 | } |
456 | | |
457 | | void flush_log(void) |
458 | 0 | { |
459 | | /* write until queue empty, but don't loop forever if there's |
460 | | no connection to the syslog in existence */ |
461 | 0 | while (log_fd != -1) |
462 | 0 | { |
463 | 0 | struct timespec waiter; |
464 | 0 | log_write(); |
465 | 0 | if (!entries || !connection_good) |
466 | 0 | { |
467 | 0 | close(log_fd); |
468 | 0 | break; |
469 | 0 | } |
470 | 0 | waiter.tv_sec = 0; |
471 | 0 | waiter.tv_nsec = 1000000; /* 1 ms */ |
472 | 0 | nanosleep(&waiter, NULL); |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | void die(char *message, char *arg1, int exit_code) |
477 | 0 | { |
478 | 0 | char *errmess = strerror(errno); |
479 | | |
480 | 0 | if (!arg1) |
481 | 0 | arg1 = errmess; |
482 | |
|
483 | 0 | if (!log_stderr) |
484 | 0 | { |
485 | 0 | echo_stderr = 1; /* print as well as log when we die.... */ |
486 | 0 | fputc('\n', stderr); /* prettyfy startup-script message */ |
487 | 0 | } |
488 | 0 | my_syslog(LOG_CRIT, message, arg1, errmess); |
489 | 0 | echo_stderr = 0; |
490 | 0 | my_syslog(LOG_CRIT, _("FAILED to start up")); |
491 | 0 | flush_log(); |
492 | | |
493 | 0 | exit(exit_code); |
494 | 0 | } |