Annotation of embedaddon/libpdel/sys/alog.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (c) 2001-2002 Packet Design, LLC.
                      4:  * All rights reserved.
                      5:  * 
                      6:  * Subject to the following obligations and disclaimer of warranty,
                      7:  * use and redistribution of this software, in source or object code
                      8:  * forms, with or without modifications are expressly permitted by
                      9:  * Packet Design; provided, however, that:
                     10:  * 
                     11:  *    (i)  Any and all reproductions of the source or object code
                     12:  *         must include the copyright notice above and the following
                     13:  *         disclaimer of warranties; and
                     14:  *    (ii) No rights are granted, in any manner or form, to use
                     15:  *         Packet Design trademarks, including the mark "PACKET DESIGN"
                     16:  *         on advertising, endorsements, or otherwise except as such
                     17:  *         appears in the above copyright notice or in the software.
                     18:  * 
                     19:  * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
                     20:  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
                     21:  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
                     22:  * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
                     23:  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
                     24:  * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
                     25:  * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
                     26:  * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
                     27:  * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
                     28:  * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
                     29:  * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
                     30:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
                     31:  * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
                     32:  * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
                     33:  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     34:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
                     35:  * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
                     36:  * THE POSSIBILITY OF SUCH DAMAGE.
                     37:  *
                     38:  * Author: Archie Cobbs <archie@freebsd.org>
                     39:  */
                     40: 
                     41: #include <sys/types.h>
                     42: #include <sys/param.h>
                     43: #include <sys/socket.h>
                     44: #include <sys/uio.h>
                     45: #include <sys/queue.h>
                     46: 
                     47: #include <netinet/in.h>
                     48: 
                     49: #include <stdlib.h>
                     50: #include <stddef.h>
                     51: #include <stdio.h>
                     52: #include <syslog.h>
                     53: #include <assert.h>
                     54: #include <string.h>
                     55: #include <stdarg.h>
                     56: #include <unistd.h>
                     57: #include <fcntl.h>
                     58: #include <errno.h>
                     59: #include <regex.h>
                     60: #include <pthread.h>
                     61: 
                     62: #include "structs/structs.h"
                     63: #include "structs/type/array.h"
                     64: #include "structs/type/int.h"
                     65: #include "structs/type/ip4.h"
                     66: #include "structs/type/pointer.h"
                     67: #include "structs/type/string.h"
                     68: #include "structs/type/struct.h"
                     69: #include "structs/type/time.h"
                     70: #include "sys/alog.h"
                     71: #include "sys/logfile.h"
                     72: #include "util/typed_mem.h"
                     73: 
                     74: /* Memory allocation types */
                     75: #define MEM_TYPE               "alog"
                     76: #define MEM_TYPE_ENTRY         "alog_entry"
                     77: #define MEM_TYPE_HISTORY       "alog_history"
                     78: #define MEM_TYPE_CURCHAN       "alog_curchan"
                     79: 
                     80: /* Last message timeout functions */
                     81: #define LASTMSG_TIMEOUT_INITIAL        5
                     82: #define LASTMSG_TIMEOUT_1(t, d)        ((t) + (d) + 1 / 2)
                     83: #define LASTMSG_TIMEOUT_2(t, d)        ((t) + (d) + 1 / 2)
                     84: #define LASTMSG_TIMEOUT_3(t)   (t)
                     85: 
                     86: #define LASTMSG_REPEATED_FMT   "last message repeated %u times"
                     87: 
                     88: /* Misc defs */
                     89: #define SYSLOG_PORT            514     /* syslogd udp port */
                     90: #define MAX_NAME               64      /* max identifier length */
                     91: #define EST_ENTRY_SIZE         100     /* estimated avg. size per entry */
                     92: 
                     93: enum alog_type {
                     94:        ALOG_NONE = 0,                  /* not initialized */
                     95:        ALOG_NULL,                      /* log to nowhere */
                     96:        ALOG_STDERR,                    /* log to stderr */
                     97:        ALOG_SYSLOG_LOCAL,              /* log to local machine syslog */
                     98:        ALOG_SYSLOG_REMOTE              /* log to remote machine syslog */
                     99: };
                    100: 
                    101: /* Current alog channel (thread-specific variable) */
                    102: struct alog_curchan {
                    103:        int             channel;
                    104: };
                    105: 
                    106: /* Stolen from syslog.h */
                    107: struct nameval {
                    108:        const char      *name;
                    109:        int             val;
                    110: };
                    111: 
                    112: /* 'Last message' state for a channel */
                    113: struct alog_lastmsg {
                    114:        char                    *msg;           /* previously logged message */
                    115:        int                     sev;            /* severity for 'msg' */
                    116:        time_t                  when;           /* timestamp for 'msg' */
                    117:        u_int                   repeat;         /* number of unlogged repeats */
                    118:        u_int                   timeout;        /* timeout for logging repeat */
                    119:        time_t                  timer_expiry;   /* timer running & expiry */
                    120: };
                    121: 
                    122: /* Structure describing one channel */
                    123: struct alog_channel {
                    124:        char                    name[MAX_NAME]; /* local syslog(3) identifier */
                    125:        struct logfile          *logfile;       /* history logfile, or NULL */
                    126:        enum alog_type          type;           /* channel configuration type */
                    127:        int                     sock;           /* remote syslog(3) socket */
                    128:        int                     facility;       /* channel facility */
                    129:        int                     min_severity;   /* min channel severity */
                    130:        u_char                  debug;          /* debug enabled flag */
                    131:        pthread_mutex_t         mutex;          /* mutex, if not ALOG_NONE */
                    132:        struct alog_lastmsg     last;           /* 'last message' info */
                    133: };
                    134: 
                    135: /*
                    136:  * Internal variables
                    137:  */
                    138: static struct          alog_channel alog_channels[ALOG_MAX_CHANNELS];
                    139: static pthread_key_t   alog_current_channel;
                    140: 
                    141: /* Stolen from syslog.h */
                    142: static struct  nameval prioritynames[] = {
                    143:        { "alert",      LOG_ALERT,      },
                    144:        { "crit",       LOG_CRIT,       },
                    145:        { "debug",      LOG_DEBUG,      },
                    146:        { "emerg",      LOG_EMERG,      },
                    147:        { "err",        LOG_ERR,        },
                    148:        { "error",      LOG_ERR,        },      /* DEPRECATED */
                    149:        { "info",       LOG_INFO,       },
                    150:        { "notice",     LOG_NOTICE,     },
                    151:        { "panic",      LOG_EMERG,      },      /* DEPRECATED */
                    152:        { "warn",       LOG_WARNING,    },      /* DEPRECATED */
                    153:        { "warning",    LOG_WARNING,    },
                    154:        { NULL,         -1,             }
                    155: };
                    156: 
                    157: /* Stolen from syslog.h */
                    158: static struct  nameval facilitynames[] = {
                    159:        { "auth",       LOG_AUTH,       },
                    160:        { "authpriv",   LOG_AUTHPRIV,   },
                    161:        { "cron",       LOG_CRON,       },
                    162:        { "daemon",     LOG_DAEMON,     },
                    163:        { "ftp",        LOG_FTP,        },
                    164:        { "kern",       LOG_KERN,       },
                    165:        { "lpr",        LOG_LPR,        },
                    166:        { "mail",       LOG_MAIL,       },
                    167:        { "news",       LOG_NEWS,       },
                    168: #ifdef LOG_NTP
                    169:        { "ntp",        LOG_NTP,        },
                    170: #endif
                    171: #ifdef LOG_SECURITY
                    172:        { "security",   LOG_SECURITY,   },
                    173: #endif
                    174:        { "syslog",     LOG_SYSLOG,     },
                    175:        { "user",       LOG_USER,       },
                    176:        { "uucp",       LOG_UUCP,       },
                    177:        { "local0",     LOG_LOCAL0,     },
                    178:        { "local1",     LOG_LOCAL1,     },
                    179:        { "local2",     LOG_LOCAL2,     },
                    180:        { "local3",     LOG_LOCAL3,     },
                    181:        { "local4",     LOG_LOCAL4,     },
                    182:        { "local5",     LOG_LOCAL5,     },
                    183:        { "local6",     LOG_LOCAL6,     },
                    184:        { "local7",     LOG_LOCAL7,     },
                    185:        { NULL,         -1,             }
                    186: };
                    187: 
                    188: /*
                    189:  * Internal functions
                    190:  */
                    191: static void    alog_write(struct alog_channel *ch,
                    192:                        int sev, time_t when, const char *msg);
                    193: static void    alog_flush_lastmsg(struct alog_channel *ch, u_int timeout);
                    194: static void    alog_last_check(struct alog_channel *ch);
                    195: static int     alog_get_channel(void);
                    196: static struct  alog_curchan *alog_get_current_channel(int create);
                    197: static void    alog_init_current_channel(void);
                    198: static void    alog_curchan_destroy(void *arg);
                    199: 
                    200: /*
                    201:  * Initialize or reconfigure a logging channel.
                    202:  */
                    203: int
                    204: alog_configure(int channel, const struct alog_config *conf)
                    205: {
                    206:        struct alog_channel *const ch = &alog_channels[channel];
                    207:        struct sockaddr_in sin;
                    208:        int init_mutex = 0;
                    209:        int esave;
                    210:        int debug;
                    211: 
                    212:        /* Sanity check */
                    213:        if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
                    214:                errno = EINVAL;
                    215:                return (-1);
                    216:        }
                    217: 
                    218:        /* If already initialized, shut it down */
                    219:        if (ch->type != ALOG_NONE)
                    220:                alog_shutdown(channel);
                    221: 
                    222:        /* Initialize channel */
                    223:        debug = ch->debug;
                    224:        memset(ch, 0, sizeof(*ch));
                    225:        ch->sock = -1;
                    226:        ch->debug = debug;
                    227:        if (conf->name != NULL)
                    228:                strlcpy(ch->name, conf->name, sizeof(ch->name));
                    229:        ch->facility = (ch->debug || conf->facility == NULL) ?
                    230:            -1 : alog_facility(conf->facility);
                    231:        ch->min_severity = conf->min_severity;
                    232: 
                    233:        /* Open logfile */
                    234:        if (conf->histlen > 0) {
                    235:                if ((ch->logfile = logfile_open(conf->path, 0,
                    236:                    conf->histlen, conf->histlen * EST_ENTRY_SIZE)) == NULL)
                    237:                        goto fail;
                    238:        }
                    239: 
                    240:        /* Initialize mutex */
                    241:        if ((errno = pthread_mutex_init(&ch->mutex, NULL)) != 0)
                    242:                goto fail;
                    243:        init_mutex = 1;
                    244: 
                    245:        /* Handle stderr case */
                    246:        if (ch->facility == -1) {
                    247:                ch->type = ALOG_STDERR;
                    248:                return (0);
                    249:        }
                    250: 
                    251:        /* Handle NULL case */
                    252:        if (conf->name == NULL) {
                    253:                ch->type = ALOG_NULL;
                    254:                return (0);
                    255:        }
                    256: 
                    257:        /* Handle local syslog case */
                    258:        if (conf->remote_server.s_addr == 0) {
                    259:                ch->type = ALOG_SYSLOG_LOCAL;
                    260:                return (0);
                    261:        }
                    262: 
                    263:        /* Handle remote syslog case */
                    264:        if ((ch->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
                    265:                goto fail;
                    266:        (void)fcntl(ch->sock, F_SETFD, 1);
                    267:        if (shutdown(ch->sock, SHUT_RD) == -1)
                    268:                goto fail;
                    269:        memset(&sin, 0, sizeof(sin));
                    270: #ifndef __linux__
                    271:        sin.sin_len = sizeof(sin);
                    272: #endif
                    273:        sin.sin_family = AF_INET;
                    274:        sin.sin_port = htons(SYSLOG_PORT);
                    275:        sin.sin_addr = conf->remote_server;
                    276:        if (connect(ch->sock, (struct sockaddr *)&sin, sizeof(sin)) == -1)
                    277:                goto fail;
                    278:        ch->type = ALOG_SYSLOG_REMOTE;
                    279:        return (0);
                    280: 
                    281: fail:
                    282:        /* Clean up after failure */
                    283:        esave = errno;
                    284:        logfile_close(&ch->logfile);                    /* ok if null */
                    285:        if (ch->sock != -1) {
                    286:                (void)close(ch->sock);
                    287:                ch->sock = -1;
                    288:        }
                    289:        if (init_mutex)
                    290:                pthread_mutex_destroy(&ch->mutex);
                    291:        errno = esave;
                    292:        return (-1);
                    293: }
                    294: 
                    295: /*
                    296:  * Shutdown one logging channel.
                    297:  */
                    298: int
                    299: alog_shutdown(int channel)
                    300: {
                    301:        struct alog_channel *const ch = &alog_channels[channel];
                    302: 
                    303:        /* Sanity check */
                    304:        if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
                    305:                errno = EINVAL;
                    306:                return (-1);
                    307:        }
                    308: 
                    309:        /* Cancel timer */
                    310:        ch->last.timer_expiry = 0;
                    311: 
                    312:        /* Flush any repeated message */
                    313:        alog_flush_lastmsg(ch, 0);
                    314: 
                    315:        /* Shut down channel */
                    316:        switch (ch->type) {
                    317:        case ALOG_SYSLOG_REMOTE:
                    318:                (void)close(ch->sock);
                    319:                ch->sock = -1;
                    320:                pthread_mutex_destroy(&ch->mutex);
                    321:                break;
                    322:        case ALOG_SYSLOG_LOCAL:
                    323:                closelog();
                    324:                pthread_mutex_destroy(&ch->mutex);
                    325:                break;
                    326:        case ALOG_NULL:
                    327:        case ALOG_STDERR:
                    328:                pthread_mutex_destroy(&ch->mutex);
                    329:                break;
                    330:        case ALOG_NONE:
                    331:                break;
                    332:        }
                    333:        ch->type = ALOG_NONE;
                    334: 
                    335:        /* Free saved last message */
                    336:        FREE(MEM_TYPE, ch->last.msg);
                    337:        ch->last.msg = NULL;
                    338: 
                    339:        /* Close logfile */
                    340:        logfile_close(&ch->logfile);                    /* ok if null */
                    341: 
                    342:        /* Done */
                    343:        return (0);
                    344: }
                    345: 
                    346: /*
                    347:  * Get current channel.
                    348:  */
                    349: static int
                    350: alog_get_channel(void)
                    351: {
                    352:        struct alog_curchan *cc;
                    353: 
                    354:        if ((cc = alog_get_current_channel(0)) == NULL)
                    355:                return (0);
                    356:        return (cc->channel);
                    357: }
                    358: 
                    359: /*
                    360:  * Set active channel.
                    361:  */
                    362: int
                    363: alog_set_channel(int channel)
                    364: {
                    365:        struct alog_curchan *cc;
                    366: 
                    367:        /* Sanity check */
                    368:        if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
                    369:                errno = EINVAL;
                    370:                return (-1);
                    371:        }
                    372: 
                    373:        /* Compare with current channel */
                    374:        if ((cc = alog_get_current_channel(channel != 0)) == NULL) {
                    375:                if (channel != 0)
                    376:                        return (-1);
                    377:                return (0);
                    378:        }
                    379:        if (channel == cc->channel)
                    380:                return (0);
                    381: 
                    382:        /* Set to new channel, even if it's uninitialized */
                    383:        cc->channel = channel;
                    384: 
                    385:        /* Done */
                    386:        return (0);
                    387: }
                    388: 
                    389: /*
                    390:  * Get current channel per-thread variable.
                    391:  */
                    392: static struct alog_curchan *
                    393: alog_get_current_channel(int create)
                    394: {
                    395:        static pthread_once_t init_channel_once = PTHREAD_ONCE_INIT;
                    396:        struct alog_curchan *cc;
                    397: 
                    398:        /* Initialize per-thread variable (once) */
                    399:        if ((errno = pthread_once(&init_channel_once,
                    400:            alog_init_current_channel)) != 0) {
                    401:                fprintf(stderr, "%s: %s: %s\n",
                    402:                    "alog", "pthread_once", strerror(errno));
                    403:                return (NULL);
                    404:        }
                    405: 
                    406:        /* Get instance for this thread; create if necessary */
                    407:        if ((cc = pthread_getspecific(alog_current_channel)) == NULL) {
                    408:                if (!create)
                    409:                        return (NULL);
                    410:                if ((cc = MALLOC(MEM_TYPE_CURCHAN, sizeof(*cc))) == NULL) {
                    411:                        fprintf(stderr, "%s: %s: %s\n",
                    412:                            "alog", "malloc", strerror(errno));
                    413:                        return (NULL);
                    414:                }
                    415:                cc->channel = 0;
                    416:                if ((errno = pthread_setspecific(alog_current_channel,
                    417:                    cc)) != 0) {
                    418:                        fprintf(stderr, "%s: %s: %s\n",
                    419:                            "alog", "pthread_setspecific", strerror(errno));
                    420:                        FREE(MEM_TYPE_CURCHAN, cc);
                    421:                        return (NULL);
                    422:                }
                    423:        }
                    424: 
                    425:        /* Return current channel */
                    426:        return (cc);
                    427: }
                    428: 
                    429: /*
                    430:  * Initialize "current channel" per-thread variable.
                    431:  */
                    432: static void
                    433: alog_init_current_channel(void)
                    434: {
                    435:        int err;
                    436: 
                    437:        if ((err = pthread_key_create(&alog_current_channel,
                    438:            alog_curchan_destroy)) != 0) {
                    439:                fprintf(stderr, "%s: %s: %s\n",
                    440:                    "alog", "pthread_key_create", strerror(err));
                    441:                assert(0);
                    442:        }
                    443: }
                    444: 
                    445: /*
                    446:  * Destroy an instance of the per-thread current channel variable.
                    447:  */
                    448: static void
                    449: alog_curchan_destroy(void *arg)
                    450: {
                    451:        struct alog_curchan *const cc = arg;
                    452: 
                    453:        FREE(MEM_TYPE_CURCHAN, cc);
                    454: }
                    455: 
                    456: /*
                    457:  * Enable debugging on a channel.
                    458:  */
                    459: void
                    460: alog_set_debug(int channel, int enabled)
                    461: {
                    462:        struct alog_channel *const ch = &alog_channels[channel];
                    463: 
                    464:        ch->debug = !!enabled;
                    465: }
                    466: 
                    467: /*
                    468:  * Log something to the currently selected channel. Preserves errno.
                    469:  */
                    470: void
                    471: alog(int sev, const char *fmt, ...)
                    472: {
                    473:        va_list args;
                    474: 
                    475:        va_start(args, fmt);
                    476:        valog(sev, fmt, args);
                    477:        va_end(args);
                    478: }
                    479: 
                    480: /*
                    481:  * Log something to the currently selected channel. Preserves errno.
                    482:  */
                    483: void
                    484: valog(int sev, const char *fmt, va_list args)
                    485: {
                    486:        const int errno_save = errno;
                    487:        struct alog_channel *const ch = &alog_channels[alog_get_channel()];
                    488:        char fmtbuf[1024];
                    489:        time_t now;
                    490:        char *msg;
                    491:        int r;
                    492: 
                    493:        /* Lock channel */
                    494:        if (ch->type != ALOG_NONE) {
                    495:                r = pthread_mutex_lock(&ch->mutex);
                    496:                assert(r == 0);
                    497:        }
                    498: 
                    499:        /* Check last message timer */
                    500:        alog_last_check(ch);
                    501: 
                    502:        /* Filter out unwanted verbosity */
                    503:        if (ch->type != ALOG_NONE && sev > ch->min_severity)
                    504:                goto done;
                    505: 
                    506:        /* Allow for things like ``alog(LOG_DEBUG + 1, ...)'' */
                    507:        if (sev >= LOG_DEBUG)
                    508:                sev = LOG_DEBUG;
                    509: 
                    510:        /* Build the message */
                    511:        alog_expand(fmt, errno_save, fmtbuf, sizeof(fmtbuf));
                    512:        VASPRINTF(MEM_TYPE, &msg, fmtbuf, args);
                    513:        if (msg == NULL) {
                    514:                fprintf(stderr, "%s: %s: %s\n",
                    515:                    __FUNCTION__, "vasprintf", strerror(errno));
                    516:                goto done;
                    517:        }
                    518: 
                    519:        /* Get current time */
                    520:        now = time(NULL);
                    521: 
                    522:        /* If not configured, don't do the repeated messages thing */
                    523:        if (ch->type == ALOG_NONE) {
                    524:                alog_write(ch, sev, now, msg);
                    525:                FREE(MEM_TYPE, msg);
                    526:                goto done;
                    527:        }
                    528: 
                    529:        /* Handle repeated messages */
                    530:        if (ch->last.msg == NULL || strcmp(msg, ch->last.msg) != 0) {
                    531:                alog_flush_lastmsg(ch, LASTMSG_TIMEOUT_INITIAL);
                    532:                FREE(MEM_TYPE, ch->last.msg);
                    533:                ch->last.msg = msg;
                    534:                ch->last.sev = sev;
                    535:                ch->last.when = now;
                    536:                alog_write(ch, sev, now, msg);
                    537:        } else {
                    538:                time_t delay;
                    539: 
                    540:                delay = MAX(now - ch->last.when, 0);
                    541:                if (++ch->last.repeat == 1) {
                    542:                        ch->last.timeout = LASTMSG_TIMEOUT_1(
                    543:                            ch->last.timeout, delay);
                    544:                } else {
                    545:                        ch->last.timeout = LASTMSG_TIMEOUT_2(
                    546:                            ch->last.timeout, delay);
                    547:                }
                    548:                ch->last.when = now;
                    549:                ch->last.timer_expiry = now + ch->last.timeout;
                    550:                FREE(MEM_TYPE, msg);
                    551:        }
                    552: 
                    553: done:
                    554:        /* Unlock channel and restore errno */
                    555:        if (ch->type != ALOG_NONE) {
                    556:                r = pthread_mutex_unlock(&ch->mutex);
                    557:                assert(r == 0);
                    558:        }
                    559:        errno = errno_save;
                    560: }
                    561: 
                    562: /*
                    563:  * Check for expiration of 'last mesasge' timer.
                    564:  *
                    565:  * This assumes the channel is locked.
                    566:  */
                    567: static void
                    568: alog_last_check(struct alog_channel *ch)
                    569: {
                    570:        /* Is the timer "running"? */
                    571:        if (ch->last.timer_expiry == 0)
                    572:                return;
                    573: 
                    574:        /* Has the timer "expired"? */
                    575:        if (ch->last.timer_expiry > time(NULL))
                    576:                return;
                    577: 
                    578:        /* "Stop" timer */
                    579:        ch->last.timer_expiry = 0;
                    580: 
                    581:        /* Flush last message */
                    582:        alog_flush_lastmsg(ch, LASTMSG_TIMEOUT_3(ch->last.timeout));
                    583: }
                    584: 
                    585: /*
                    586:  * Log repeated message(s).
                    587:  *
                    588:  * This assumes the channel is locked.
                    589:  */
                    590: static void
                    591: alog_flush_lastmsg(struct alog_channel *ch, u_int timeout)
                    592: {
                    593:        switch (ch->last.repeat) {
                    594:        case 0:
                    595:                break;
                    596:        case 1:
                    597:                if (ch->last.msg != NULL) {
                    598:                        alog_write(ch, ch->last.sev,
                    599:                            ch->last.when, ch->last.msg);
                    600:                }
                    601:                break;
                    602:        default:
                    603:            {
                    604:                char buf[sizeof(LASTMSG_REPEATED_FMT) + 32];
                    605: 
                    606:                snprintf(buf, sizeof(buf),
                    607:                    LASTMSG_REPEATED_FMT, ch->last.repeat);
                    608:                alog_write(ch, ch->last.sev, ch->last.when, buf);
                    609:                break;
                    610:            }
                    611:        }
                    612:        ch->last.repeat = 0;
                    613:        ch->last.timeout = timeout;
                    614:        ch->last.timer_expiry = 0;
                    615: }
                    616: 
                    617: /*
                    618:  * Write a message to the log.
                    619:  *
                    620:  * This assumes the channel is locked.
                    621:  */
                    622: static void
                    623: alog_write(struct alog_channel *ch, int sev, time_t when, const char *msg)
                    624: {
                    625:        /* Log it to wherever */
                    626:        switch (ch->type) {
                    627:        case ALOG_NONE:
                    628:        case ALOG_STDERR:
                    629:                fprintf(stderr, "%s: %s\n",
                    630:                    alog_severity_name(LOG_PRI(sev)), msg);
                    631:                break;
                    632:        case ALOG_NULL:
                    633:                break;
                    634:        case ALOG_SYSLOG_LOCAL:
                    635:                openlog(ch->name, 0, ch->facility);     /* XXX race condition */
                    636:                syslog(sev, "%s", msg);                 /* XXX race condition */
                    637:                break;
                    638:        case ALOG_SYSLOG_REMOTE:
                    639:            {
                    640:                char ascbuf[32];
                    641:                struct tm tm;
                    642:                char *netmsg;
                    643:                int len;
                    644: 
                    645:                if (ch->sock == -1)
                    646:                        break;
                    647:                gmtime_r(&when, &tm);
                    648:                asctime_r(&tm, ascbuf);
                    649:                len = ASPRINTF(TYPED_MEM_TEMP, &netmsg, "<%d>%.15s %s",
                    650:                    LOG_MAKEPRI(ch->facility, sev), ascbuf + 4, msg);
                    651:                if (netmsg != NULL)
                    652:                        write(ch->sock, netmsg, len);
                    653:                FREE(TYPED_MEM_TEMP, netmsg);
                    654:                break;
                    655:            }
                    656:        }
                    657: 
                    658:        /* Save message in history buffer */
                    659:        if (ch->logfile != NULL) {
                    660:                struct alog_entry *ent;
                    661:                int mlen;
                    662: 
                    663:                /* Setup entry */
                    664:                mlen = strlen(msg) + 1;
                    665:                if ((ent = MALLOC(MEM_TYPE, sizeof(*ent) + mlen)) == NULL) {
                    666:                        fprintf(stderr, "%s: %s: %s\n",
                    667:                            __FUNCTION__, "malloc", strerror(errno));
                    668:                        return;
                    669:                }
                    670:                ent->when = when;
                    671:                ent->sev = LOG_PRI(sev);
                    672:                memcpy(ent->msg, msg, mlen);
                    673: 
                    674:                /* Add to history file */
                    675:                if (logfile_put(ch->logfile, ent, sizeof(*ent) + mlen) == -1) {
                    676:                        fprintf(stderr, "%s: %s: %s\n",
                    677:                            __FUNCTION__, "logfile_put", strerror(errno));
                    678:                }
                    679:                FREE(MEM_TYPE, ent);
                    680:        }
                    681: }
                    682: 
                    683: /*
                    684:  * Get channel history
                    685:  */
                    686: int
                    687: alog_get_history(int channel, int min_sev, int max_num, time_t max_age,
                    688:        const regex_t *preg, struct alog_history *list)
                    689: {
                    690:        struct alog_channel *const ch = &alog_channels[channel];
                    691:        struct alog_entry *copy;
                    692:        regmatch_t pmatch;
                    693:        u_int num_ent;
                    694:        int rtn = -1;
                    695:        time_t now;
                    696:        int i, j;
                    697:        u_int num;
                    698:        int r;
                    699: 
                    700:        /* Sanity check */
                    701:        if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
                    702:                errno = EINVAL;
                    703:                return (-1);
                    704:        }
                    705: 
                    706:        /* Lock channel */
                    707:        if (ch->type != ALOG_NONE) {
                    708:                r = pthread_mutex_lock(&ch->mutex);
                    709:                assert(r == 0);
                    710:        }
                    711: 
                    712:        /* Check last message timer */
                    713:        alog_last_check(ch);
                    714: 
                    715:        /* Initialize array */
                    716:        memset(list, 0, sizeof(*list));
                    717: 
                    718:        /* Nothing to return? */
                    719:        if (ch->logfile == NULL
                    720:            || max_num == 0
                    721:            || (num_ent = logfile_num_entries(ch->logfile)) == 0) {
                    722:                rtn = 0;
                    723:                goto done;
                    724:        }
                    725: 
                    726:        /* Get current time */
                    727:        now = time(NULL);
                    728: 
                    729: #define MATCH(sev, when, msg, msglen)                                  \
                    730:        (sev <= min_sev && (now - when) <= max_age && (preg == NULL     \
                    731:          || (pmatch.rm_so = 0, pmatch.rm_eo = msglen,                  \
                    732:            regexec(preg, msg, 0, &pmatch, REG_STARTEND) == 0)))
                    733: 
                    734:        /* Count how many entries we'll be returning */
                    735:        num = 0;
                    736:        if (ch->last.repeat > 0
                    737:            && ch->last.msg != NULL
                    738:            && MATCH(ch->last.sev, ch->last.when,
                    739:              ch->last.msg, strlen(ch->last.msg)))
                    740:                num++;
                    741:        for (i = -1; num < max_num && i >= -num_ent; i--) {
                    742:                const struct alog_entry *ent;
                    743:                int len;
                    744: 
                    745:                /* Get entry, check severity and age */
                    746:                if ((ent = logfile_get(ch->logfile, i, &len)) != NULL
                    747:                    && len >= sizeof(*ent) + 1
                    748:                    && MATCH(ent->sev, ent->when,
                    749:                      ent->msg, len - sizeof(*ent) - 1))
                    750:                        num++;
                    751:        }
                    752:        if (num == 0) {
                    753:                rtn = 0;
                    754:                goto done;
                    755:        }
                    756: 
                    757:        /* Allocate array of pointers */
                    758:        if ((list->elems = MALLOC(MEM_TYPE_HISTORY,
                    759:            num * sizeof(*list->elems))) == NULL)
                    760:                goto done;
                    761: 
                    762:        /* Fill array, starting with the most recent first */
                    763:        j = num;
                    764:        if (ch->last.repeat > 0
                    765:            && ch->last.msg != NULL
                    766:            && MATCH(ch->last.sev, ch->last.when,
                    767:              ch->last.msg, strlen(ch->last.msg))) {
                    768:                char buf[sizeof(LASTMSG_REPEATED_FMT) + 32];
                    769:                const char *s;
                    770: 
                    771:                if (ch->last.repeat > 1) {
                    772:                        snprintf(buf, sizeof(buf),
                    773:                            LASTMSG_REPEATED_FMT, ch->last.repeat);
                    774:                        s = buf;
                    775:                } else
                    776:                        s = ch->last.msg;
                    777:                if ((copy = MALLOC(MEM_TYPE_ENTRY,
                    778:                    sizeof(*copy) + strlen(s) + 1)) != NULL) {
                    779:                        copy->when = ch->last.when;
                    780:                        copy->sev = ch->last.sev;
                    781:                        strcpy(copy->msg, s);
                    782:                        list->elems[--j] = copy;
                    783:                }
                    784:        }
                    785:        for (i = -1; j > 0 && i >= -num_ent; i--) {
                    786:                const struct alog_entry *ent;
                    787:                int len;
                    788: 
                    789:                /* Copy it if it matches */
                    790:                if ((ent = logfile_get(ch->logfile, i, &len)) != NULL
                    791:                    && len >= sizeof(*ent) + 1
                    792:                    && MATCH(ent->sev, ent->when,
                    793:                      ent->msg, len - sizeof(*ent) - 1)) {
                    794:                        if ((copy = MALLOC(MEM_TYPE_ENTRY, len)) != NULL) {
                    795:                                memcpy(copy, ent, len - 1);
                    796:                                ((char *)copy)[len - 1] = '\0'; /* safety */
                    797:                                list->elems[--j] = copy;
                    798:                        }
                    799:                }
                    800:        }
                    801: 
                    802:        /* Collapse entries dropped because of memory problems or whatever */
                    803:        if (j > 0) {
                    804:                num -= j;
                    805:                memcpy(list->elems, list->elems + j,
                    806:                    num * sizeof(*list->elems));
                    807:        }
                    808:        list->length = num;
                    809:        rtn = 0;
                    810: 
                    811: done:
                    812:        /* Unlock channel and return */
                    813:        if (ch->type != ALOG_NONE) {
                    814:                r = pthread_mutex_unlock(&ch->mutex);
                    815:                assert(r == 0);
                    816:        }
                    817:        return (rtn);
                    818: }
                    819: 
                    820: /*
                    821:  * Clear log history.
                    822:  */
                    823: int
                    824: alog_clear_history(int channel)
                    825: {
                    826:        struct alog_channel *const ch = &alog_channels[channel];
                    827:        int r;
                    828: 
                    829:        /* Sanity check */
                    830:        if (channel < 0 || channel >= ALOG_MAX_CHANNELS) {
                    831:                errno = EINVAL;
                    832:                return (-1);
                    833:        }
                    834: 
                    835:        /* Lock channel */
                    836:        if (ch->type != ALOG_NONE) {
                    837:                r = pthread_mutex_lock(&ch->mutex);
                    838:                assert(r == 0);
                    839:        }
                    840: 
                    841:        /* Reset last message state */
                    842:        ch->last.timer_expiry = 0;
                    843:        FREE(MEM_TYPE, ch->last.msg);
                    844:        memset(&ch->last, 0, sizeof(ch->last));
                    845: 
                    846:        /* Zero out logfile */
                    847:        if (ch->logfile != NULL)
                    848:                logfile_trim(ch->logfile, 0);
                    849: 
                    850:        /* Unlock channel */
                    851:        if (ch->type != ALOG_NONE) {
                    852:                r = pthread_mutex_unlock(&ch->mutex);
                    853:                assert(r == 0);
                    854:        }
                    855: 
                    856:        /* Done */
                    857:        return (0);
                    858: }
                    859: 
                    860: /*
                    861:  * Decode syslog facility name
                    862:  */
                    863: int
                    864: alog_facility(const char *name)
                    865: {
                    866:        int i;
                    867: 
                    868:        for (i = 0; facilitynames[i].name != NULL; i++) {
                    869:                if (strcmp(name, facilitynames[i].name) == 0)
                    870:                        return (facilitynames[i].val);
                    871:        }
                    872:        return (-1);
                    873: }
                    874: 
                    875: /*
                    876:  * Decode syslog facility name
                    877:  */
                    878: const char *
                    879: alog_facility_name(int facility)
                    880: {
                    881:        int i;
                    882: 
                    883:        for (i = 0; facilitynames[i].name != NULL; i++) {
                    884:                if (facilitynames[i].val == facility)
                    885:                        return (facilitynames[i].name);
                    886:        }
                    887:        return (NULL);
                    888: }
                    889: 
                    890: /*
                    891:  * Decode syslog severity name
                    892:  */
                    893: int
                    894: alog_severity(const char *name)
                    895: {
                    896:        char *eptr;
                    897:        int i;
                    898: 
                    899:        for (i = 0; prioritynames[i].name != NULL; i++) {
                    900:                if (strcmp(name, prioritynames[i].name) == 0)
                    901:                        return (prioritynames[i].val);
                    902:        }
                    903:        i = (int)strtol(name, &eptr, 0);
                    904:        if (*name != '\0' && *eptr == '\0')
                    905:                return (i);
                    906:        return (-1);
                    907: }
                    908: 
                    909: /*
                    910:  * Decode syslog severity name
                    911:  */
                    912: const char *
                    913: alog_severity_name(int severity)
                    914: {
                    915:        int i;
                    916: 
                    917:        for (i = 0; prioritynames[i].name != NULL; i++) {
                    918:                if (prioritynames[i].val == severity)
                    919:                        return (prioritynames[i].name);
                    920:        }
                    921:        return (NULL);
                    922: }
                    923: 
                    924: /*
                    925:  * Kludge to deal with %m format when not using syslog
                    926:  */
                    927: void
                    928: alog_expand(const char *fmt, int errnum, char *buf, size_t bufsize)
                    929: {
                    930:        const char *errstr;
                    931:        const char *s;
                    932:        int errlen;
                    933:        int i;
                    934: 
                    935:        /* Find "%m" which is usually at the end */
                    936:        for (s = fmt + strlen(fmt) - 2;
                    937:            s >= fmt && !(s[0] == '%' && s[1] == 'm');
                    938:            s--);
                    939: 
                    940:        /* Check if we should bother doing anything */
                    941:        if (s < fmt || (i = s - fmt) > bufsize - 2) {
                    942:                strlcpy(buf, fmt, bufsize);
                    943:                return;
                    944:        }
                    945: 
                    946:        /* Convert "%m" to error string */
                    947:        errstr = strerror(errnum);
                    948:        errlen = strlen(errstr);
                    949:        strlcpy(buf, fmt, i + 1);
                    950:        strlcpy(buf + i, errstr, bufsize - i);
                    951:        strlcpy(buf + i + errlen, fmt + i + 2, bufsize - i - errlen);
                    952: }
                    953: 
                    954: /*********************************************************************
                    955:                        STRUCTS TYPE DEFINITIONS
                    956: *********************************************************************/
                    957: 
                    958: /* Type for "path" and "name" strings in struct alog_config */
                    959: static const struct structs_type alog_path_type
                    960:        = STRUCTS_STRING_TYPE("alog_config.path", 1);
                    961: static const struct structs_type alog_name_type
                    962:        = STRUCTS_STRING_TYPE("alog_config.name", 1);
                    963: 
                    964: /* Fields list for struct alog_config */
                    965: static const struct structs_field alog_config_fields[] = {
                    966:        STRUCTS_STRUCT_FIELD(alog_config, path, &alog_path_type),
                    967:        STRUCTS_STRUCT_FIELD(alog_config, name, &alog_name_type),
                    968:        STRUCTS_STRUCT_FIELD(alog_config, facility, &alog_facility_type),
                    969:        STRUCTS_STRUCT_FIELD(alog_config, remote_server, &structs_type_ip4),
                    970:        STRUCTS_STRUCT_FIELD(alog_config, min_severity, &alog_severity_type),
                    971:        STRUCTS_STRUCT_FIELD(alog_config, histlen, &structs_type_int),
                    972:        STRUCTS_STRUCT_FIELD_END
                    973: };
                    974: 
                    975: /* Type for struct alog_config */
                    976: const struct structs_type alog_config_type
                    977:        = STRUCTS_STRUCT_TYPE(alog_config, &alog_config_fields);
                    978: 
                    979: /* Override method for alog_facility_type */
                    980: static structs_init_t          alog_facility_type_init;
                    981: static structs_binify_t                alog_facility_type_binify;
                    982: 
                    983: /* Type for syslog facility, which we store as a string. */
                    984: const struct structs_type alog_facility_type = {
                    985:        sizeof(char *),
                    986:        "alog",
                    987:        STRUCTS_TYPE_PRIMITIVE,
                    988:        alog_facility_type_init,
                    989:        structs_ascii_copy,
                    990:        structs_string_equal,
                    991:        structs_string_ascify,
                    992:        alog_facility_type_binify,
                    993:        structs_string_encode,
                    994:        structs_string_decode,
                    995:        structs_string_free,
                    996:        { { (void *)"alog_config.facility" }, { (void *)1 } }
                    997: };
                    998: 
                    999: /*
                   1000:  * Initializer for alog_facility_type
                   1001:  */
                   1002: static int
                   1003: alog_facility_type_init(const struct structs_type *type, void *data)
                   1004: {
                   1005:        return (structs_string_binify(type, "daemon", data, NULL, 0));
                   1006: }
                   1007: 
                   1008: /*
                   1009:  * Binifier for alog_facility_type
                   1010:  */
                   1011: static int
                   1012: alog_facility_type_binify(const struct structs_type *type,
                   1013:        const char *ascii, void *data, char *ebuf, size_t emax)
                   1014: {
                   1015:        if (*ascii != '\0' && alog_facility(ascii) == -1) {
                   1016:                strlcpy(ebuf, "invalid syslog facility", emax);
                   1017:                errno = EINVAL;
                   1018:                return (-1);
                   1019:        }
                   1020:        return (structs_string_binify(type, ascii, data, NULL, 0));
                   1021: }
                   1022: 
                   1023: /* Override methods for alog_severity_type */
                   1024: static structs_init_t          alog_severity_init;
                   1025: static structs_ascify_t                alog_severity_ascify;
                   1026: static structs_binify_t                alog_severity_binify;
                   1027: 
                   1028: /* Type for severity, which defaults to LOG_INFO. The input can be either
                   1029:    a symbolic name or a numeric value. */
                   1030: const struct structs_type alog_severity_type = {
                   1031:        sizeof(int),
                   1032:        "int",
                   1033:        STRUCTS_TYPE_PRIMITIVE,
                   1034:        alog_severity_init,
                   1035:        structs_region_copy,
                   1036:        structs_region_equal,
                   1037:        alog_severity_ascify,
                   1038:        alog_severity_binify,
                   1039:        structs_region_encode_netorder,
                   1040:        structs_region_decode_netorder,
                   1041:        structs_nothing_free,
                   1042:        { { (void *)2 }, { (void *)1 } }
                   1043: };
                   1044: 
                   1045: /*
                   1046:  * Initializer for severity
                   1047:  */
                   1048: static int
                   1049: alog_severity_init(const struct structs_type *type, void *data)
                   1050: {
                   1051:        *((int *)data) = LOG_INFO;
                   1052:        return (0);
                   1053: }
                   1054: 
                   1055: /*
                   1056:  * Ascifier for severity
                   1057:  */
                   1058: static char *
                   1059: alog_severity_ascify(const struct structs_type *type,
                   1060:        const char *mtype, const void *data)
                   1061: {
                   1062:        const int sev = *((int *)data);
                   1063:        const char *s;
                   1064:        char buf[32];
                   1065: 
                   1066:        if ((s = alog_severity_name(sev)) == NULL) {
                   1067:                snprintf(buf, sizeof(buf), "%d", sev);
                   1068:                s = buf;
                   1069:        }
                   1070:        return (STRDUP(mtype, s));
                   1071: }
                   1072: 
                   1073: /*
                   1074:  * Binifier for severity
                   1075:  */
                   1076: static int
                   1077: alog_severity_binify(const struct structs_type *type,
                   1078:        const char *ascii, void *data, char *ebuf, size_t emax)
                   1079: {
                   1080:        int sev;
                   1081: 
                   1082:        if ((sev = alog_severity(ascii)) == -1
                   1083:            && structs_int_binify(type, ascii, data, NULL, 0) == -1) {
                   1084:                strlcpy(ebuf, "invalid syslog severity", emax);
                   1085:                return (-1);
                   1086:        }
                   1087:        *((int *)data) = sev;
                   1088:        return (0);
                   1089: }
                   1090: 
                   1091: /*
                   1092:  * Ascifier for log entry message.
                   1093:  */
                   1094: static char *
                   1095: alog_entry_msg_ascify(const struct structs_type *type,
                   1096:        const char *mtype, const void *data)
                   1097: {
                   1098:        return (STRDUP(mtype, data));
                   1099: }
                   1100: 
                   1101: /* Type for log entry message. This is a "read only" type. */
                   1102: static const struct structs_type alog_entry_msg_type = {
                   1103:        0,
                   1104:        "alog_entry_msg_type",
                   1105:        STRUCTS_TYPE_PRIMITIVE,
                   1106:        structs_notsupp_init,
                   1107:        structs_notsupp_copy,
                   1108:        structs_notsupp_equal,
                   1109:        alog_entry_msg_ascify,
                   1110:        structs_notsupp_binify,
                   1111:        structs_notsupp_encode,
                   1112:        structs_notsupp_decode,
                   1113:        structs_nothing_free,
                   1114: };
                   1115: 
                   1116: /* Type for history list: an array of pointers to log entries */
                   1117: static const struct structs_field alog_entry_fields[] = {
                   1118:        STRUCTS_STRUCT_FIELD(alog_entry, when, &structs_type_time_abs),
                   1119:        STRUCTS_STRUCT_FIELD(alog_entry, sev, &alog_severity_type),
                   1120:        STRUCTS_STRUCT_FIELD(alog_entry, msg, &alog_entry_msg_type),
                   1121:        STRUCTS_STRUCT_FIELD_END
                   1122: };
                   1123: static const struct structs_type alog_entry_type
                   1124:        = STRUCTS_STRUCT_TYPE(alog_entry, &alog_entry_fields);
                   1125: static const struct structs_type alog_history_ptr_type
                   1126:        = STRUCTS_POINTER_TYPE(&alog_entry_type, MEM_TYPE_ENTRY);
                   1127: const struct structs_type alog_history_type
                   1128:        = STRUCTS_ARRAY_TYPE(&alog_history_ptr_type, MEM_TYPE_HISTORY, "entry");
                   1129: 

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