Annotation of embedaddon/dnsmasq/src/log.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>