2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <time.h>
2N/A#include <syslog.h>
2N/A#include <thread.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stdarg.h>
2N/A#include <dlfcn.h>
2N/A#include <sys/synch.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/errno.h>
2N/A#include <ctype.h>
2N/A#include <smbsrv/ndl/eventlog.ndl>
2N/A#include <smbsrv/libntsvcs.h>
2N/A
2N/Atypedef struct logr_eventlog {
2N/A const char *el_name;
2N/A const char *el_path;
2N/A} logr_eventlog_t;
2N/A
2N/Alogr_eventlog_t logr_eventlog[] = {
2N/A { "System", "/var/adm/messages" },
2N/A { "smbd", "/var/smb/smbd_log.txt" }
2N/A};
2N/A
2N/Atypedef enum {
2N/A LOGR_MONTH = 0,
2N/A LOGR_DAY,
2N/A LOGR_TIME,
2N/A LOGR_HOST,
2N/A LOGR_SOURCE,
2N/A LOGR_IDTAG,
2N/A LOGR_ID,
2N/A LOGR_PRI_FAC,
2N/A LOGR_NARG
2N/A} logr_syslog_tokens_t;
2N/A
2N/A/*
2N/A * Event code translation struct for use in processing config file
2N/A */
2N/Atypedef struct logr_priority {
2N/A char *p_name;
2N/A int p_value;
2N/A} logr_priority_t;
2N/A
2N/Astatic logr_priority_t logr_pri_names[] = {
2N/A "panic", LOG_EMERG,
2N/A "emerg", LOG_EMERG,
2N/A "alert", LOG_ALERT,
2N/A "crit", LOG_CRIT,
2N/A "err", LOG_ERR,
2N/A "error", LOG_ERR,
2N/A "warn", LOG_WARNING,
2N/A "warning", LOG_WARNING,
2N/A "notice", LOG_NOTICE,
2N/A "info", LOG_INFO,
2N/A "debug", LOG_DEBUG
2N/A};
2N/A
2N/Atypedef struct logr_syslog_node {
2N/A list_node_t ln_node;
2N/A char ln_logline[LOGR_MAXENTRYLEN];
2N/A} logr_syslog_node_t;
2N/A
2N/Astatic void *logr_interposer_hdl = NULL;
2N/Astatic struct {
2N/A boolean_t (*logr_op_supported)(char *);
2N/A int (*logr_op_snapshot)(logr_context_t *);
2N/A} logr_interposer_ops;
2N/A
2N/A/*
2N/A * Set the syslog timestamp.
2N/A *
2N/A * This is a private helper for logr_syslog_parse_entry(), which
2N/A * must ensure that the appropriate argv entries are non-null.
2N/A */
2N/Astatic void
2N/Alogr_syslog_set_timestamp(char **argv, logr_entry_t *le)
2N/A{
2N/A char *month = argv[LOGR_MONTH];
2N/A char *day = argv[LOGR_DAY];
2N/A char *time = argv[LOGR_TIME];
2N/A struct timeval now;
2N/A struct tm tm, cur_tm;
2N/A char buf[32];
2N/A
2N/A bzero(&tm, sizeof (tm));
2N/A (void) snprintf(buf, 32, "%s %s %s", month, day, time);
2N/A if (strptime(buf, "%b" "%d" "%H:%M:%S", &tm) == NULL) {
2N/A le->le_timestamp.tv_sec = 0;
2N/A return;
2N/A }
2N/A
2N/A (void) gettimeofday(&now, NULL);
2N/A (void) localtime_r(&now.tv_sec, &cur_tm);
2N/A
2N/A tm.tm_isdst = cur_tm.tm_isdst;
2N/A tm.tm_year = cur_tm.tm_year;
2N/A if (tm.tm_mon > cur_tm.tm_mon)
2N/A tm.tm_year--;
2N/A
2N/A le->le_timestamp.tv_sec = mktime(&tm);
2N/A}
2N/A
2N/A/*
2N/A * Set the syslog priority.
2N/A *
2N/A * This is a private helper for logr_syslog_parse_entry(), which
2N/A * must ensure that the appropriate argv entries are non-null.
2N/A */
2N/Astatic void
2N/Alogr_syslog_set_priority(char **argv, logr_entry_t *le)
2N/A{
2N/A logr_priority_t *entry;
2N/A char *token;
2N/A int sz = sizeof (logr_pri_names) / sizeof (logr_pri_names[0]);
2N/A int i;
2N/A
2N/A le->le_pri = LOG_INFO;
2N/A
2N/A if ((token = argv[LOGR_PRI_FAC]) == NULL)
2N/A return;
2N/A
2N/A for (i = 0; i < sz; i++) {
2N/A entry = &logr_pri_names[i];
2N/A
2N/A if (strstr(token, entry->p_name) != NULL) {
2N/A le->le_pri = entry->p_value;
2N/A break;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Parse a syslog entry into a log_entry_t structure. A typical syslog
2N/A * entry has one of the following formats:
2N/A *
2N/A * <month> <day> <time> <host> <msg>
2N/A * <month> <day> <time> <host> <source>: [ID <ID> <facility.priority>] <msg>
2N/A *
2N/A * For Example:
2N/A * Oct 29 09:49:20 galaxy smbd[104039]: [ID 702911 daemon.info] init done
2N/A */
2N/Astatic int
2N/Alogr_syslog_parse_entry(char *logline, logr_entry_t *le)
2N/A{
2N/A char buf[LOGR_MAXENTRYLEN];
2N/A char *argv[LOGR_NARG];
2N/A char *value;
2N/A char *bp;
2N/A int i;
2N/A
2N/A (void) memset(argv, 0, sizeof (char *) * LOGR_NARG);
2N/A (void) strlcpy(buf, logline, LOGR_MAXENTRYLEN);
2N/A
2N/A for (bp = buf, i = 0; i < LOGR_NARG; ++i) {
2N/A if (i == LOGR_SOURCE) {
2N/A /*
2N/A * If the [ID key is not present, everything
2N/A * that follows is the message text.
2N/A */
2N/A if (strstr(bp, "[ID") == NULL)
2N/A break;
2N/A }
2N/A
2N/A do {
2N/A if ((value = strsep(&bp, " \t")) == NULL)
2N/A break;
2N/A } while (*value == '\0');
2N/A
2N/A if ((argv[i] = value) == NULL)
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * bp should be pointing at the remaining message text.
2N/A */
2N/A if ((value = strchr(bp, '\n')) != NULL)
2N/A *value = '\0';
2N/A
2N/A (void) strlcpy(le->le_msg, bp, LOGR_MAXENTRYLEN);
2N/A (void) strlcpy(le->le_hostname, argv[LOGR_HOST], MAXHOSTNAMELEN);
2N/A logr_syslog_set_timestamp(argv, le);
2N/A logr_syslog_set_priority(argv, le);
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Alogr_syslog_destroy_queue(list_t *queue)
2N/A{
2N/A logr_syslog_node_t *head;
2N/A
2N/A while ((head = list_head(queue)) != NULL) {
2N/A list_remove(queue, head);
2N/A free(head);
2N/A }
2N/A list_destroy(queue);
2N/A}
2N/A
2N/Astatic int
2N/Alogr_syslog_construct_queue(FILE *fp, list_t *queue)
2N/A{
2N/A logr_syslog_node_t *node, *head;
2N/A int line_num = 0;
2N/A char logline[LOGR_MAXENTRYLEN];
2N/A
2N/A list_create(queue, sizeof (logr_syslog_node_t),
2N/A offsetof(logr_syslog_node_t, ln_node));
2N/A
2N/A bzero(logline, LOGR_MAXENTRYLEN);
2N/A while (fgets(logline, LOGR_MAXENTRYLEN, fp) != NULL) {
2N/A /* Read the last 1024 entries in the queue */
2N/A if (line_num > LOGR_NMSGMASK) {
2N/A head = list_head(queue);
2N/A list_remove(queue, head);
2N/A free(head);
2N/A }
2N/A
2N/A if ((node = malloc(sizeof (logr_syslog_node_t))) == NULL) {
2N/A logr_syslog_destroy_queue(queue);
2N/A return (-1);
2N/A }
2N/A bzero(node->ln_logline, LOGR_MAXENTRYLEN);
2N/A
2N/A (void) strlcpy(node->ln_logline, logline, LOGR_MAXENTRYLEN);
2N/A list_insert_tail(queue, node);
2N/A bzero(logline, LOGR_MAXENTRYLEN);
2N/A line_num++;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * logr_syslog_load
2N/A *
2N/A * Loads the given log file into log_info_t structure format.
2N/A *
2N/A * Returns pointer to the allocated log structure on success.
2N/A * Note that the caller is responsible for freeing the allocated
2N/A * memory for returned log_info_t structure.
2N/A */
2N/Astatic int
2N/Alogr_syslog_load(FILE *fp, logr_info_t *log)
2N/A{
2N/A logr_entry_t *entry;
2N/A int i = 0;
2N/A
2N/A list_t queue;
2N/A logr_syslog_node_t *node;
2N/A
2N/A if (logr_syslog_construct_queue(fp, &queue) < 0)
2N/A return (-1);
2N/A
2N/A node = list_head(&queue);
2N/A while (node) {
2N/A entry = &log->li_entry[i];
2N/A
2N/A if (logr_syslog_parse_entry(node->ln_logline, entry) != 0) {
2N/A node = list_next(&queue, node);
2N/A continue;
2N/A }
2N/A
2N/A if (++i > LOGR_NMSGMASK)
2N/A break;
2N/A
2N/A node = list_next(&queue, node);
2N/A }
2N/A
2N/A logr_syslog_destroy_queue(&queue);
2N/A log->li_idx = i;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * logr_syslog_snapshot
2N/A *
2N/A * Return a snapshot of the given log in the buffer
2N/A * provided by the caller. Returns the number of entries in
2N/A * the log.
2N/A */
2N/Astatic int
2N/Alogr_syslog_snapshot(char *logname, logr_info_t *loginfo)
2N/A{
2N/A FILE *fp;
2N/A char path[MAXPATHLEN];
2N/A int i;
2N/A
2N/A if ((loginfo == NULL) || (!logr_is_supported(logname)))
2N/A return (-1);
2N/A
2N/A path[0] = '\0';
2N/A for (i = 0; i < sizeof (logr_eventlog)/sizeof (logr_eventlog[0]); ++i) {
2N/A if (strcasecmp(logname, logr_eventlog[i].el_name) == 0)
2N/A (void) strlcpy(path, logr_eventlog[i].el_path,
2N/A MAXPATHLEN);
2N/A }
2N/A
2N/A if ((fp = fopen(path, "r")) == 0)
2N/A return (-1);
2N/A
2N/A if (logr_syslog_load(fp, loginfo) < 0) {
2N/A (void) fclose(fp);
2N/A return (-1);
2N/A }
2N/A (void) fclose(fp);
2N/A
2N/A if (loginfo->li_idx <= LOGR_NMSGMASK)
2N/A return (loginfo->li_idx);
2N/A
2N/A return (LOGR_NMSGMASK+1);
2N/A}
2N/A
2N/A/*
2N/A * logr_is_supported
2N/A *
2N/A * Determines if a given log is supported or not.
2N/A * Returns B_TRUE on success, B_FALSE on failure.
2N/A */
2N/Aboolean_t
2N/Alogr_is_supported(char *log_name)
2N/A{
2N/A int i;
2N/A
2N/A if (log_name == NULL)
2N/A return (B_FALSE);
2N/A
2N/A if (logr_interposer_ops.logr_op_supported != NULL)
2N/A return (logr_interposer_ops.logr_op_supported(log_name));
2N/A
2N/A for (i = 0; i < sizeof (logr_eventlog)/sizeof (logr_eventlog[0]); ++i) {
2N/A if (strcasecmp(log_name, logr_eventlog[i].el_name) == 0)
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * logr_get_snapshot
2N/A *
2N/A * Allocate memory and make a copy, as a snapshot, from system log.
2N/A * Returns 0 on success, -1 on failure.
2N/A */
2N/Aint
2N/Alogr_get_snapshot(logr_context_t *ctx)
2N/A{
2N/A logr_read_data_t *data = NULL;
2N/A
2N/A if (logr_interposer_ops.logr_op_snapshot != NULL)
2N/A return (logr_interposer_ops.logr_op_snapshot(ctx));
2N/A
2N/A ctx->lc_cached_read_data = malloc(sizeof (logr_read_data_t));
2N/A if (ctx->lc_cached_read_data != NULL) {
2N/A data = ctx->lc_cached_read_data;
2N/A
2N/A data->rd_log = (logr_info_t *)malloc(sizeof (logr_info_t));
2N/A if (data->rd_log == NULL) {
2N/A free(data);
2N/A return (-1);
2N/A }
2N/A bzero(data->rd_log, sizeof (logr_info_t));
2N/A
2N/A data->rd_tot_recnum = logr_syslog_snapshot(ctx->lc_source_name,
2N/A data->rd_log);
2N/A if (data->rd_tot_recnum < 0) {
2N/A free(data->rd_log);
2N/A free(data);
2N/A return (-1);
2N/A }
2N/A
2N/A data->rd_first_read = 1;
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * logr_init
2N/A *
2N/A * Initializes the Eventlog service.
2N/A * Checks to see if a event log utility library
2N/A * is interposed. If yes then it'll initializes logr_interposer_ops
2N/A * structure with function pointers from this library.
2N/A */
2N/Avoid
2N/Alogr_init(void)
2N/A{
2N/A logr_interposer_hdl = smb_dlopen();
2N/A if (logr_interposer_hdl == NULL)
2N/A return;
2N/A
2N/A bzero((void *)&logr_interposer_ops, sizeof (logr_interposer_ops));
2N/A
2N/A logr_interposer_ops.logr_op_supported =
2N/A (boolean_t (*)())dlsym(logr_interposer_hdl, "logr_is_supported");
2N/A
2N/A logr_interposer_ops.logr_op_snapshot =
2N/A (int (*)())dlsym(logr_interposer_hdl, "logr_get_snapshot");
2N/A
2N/A if (logr_interposer_ops.logr_op_supported == NULL ||
2N/A logr_interposer_ops.logr_op_snapshot == NULL)
2N/A logr_fini();
2N/A}
2N/A
2N/A/*
2N/A * logr_fini
2N/A *
2N/A * Finalizes the Eventlog service.
2N/A * Closes handle to interposed library.
2N/A */
2N/Avoid
2N/Alogr_fini(void)
2N/A{
2N/A smb_dlclose(logr_interposer_hdl);
2N/A logr_interposer_hdl = NULL;
2N/A bzero((void *)&logr_interposer_ops, sizeof (logr_interposer_ops));
2N/A}