/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "structs/structs.h" #include "structs/type/array.h" #include "structs/type/int.h" #include "structs/type/ip4.h" #include "structs/type/pointer.h" #include "structs/type/string.h" #include "structs/type/struct.h" #include "structs/type/time.h" #include "sys/alog.h" #include "sys/logfile.h" #include "util/typed_mem.h" /* Memory allocation types */ #define MEM_TYPE "alog" #define MEM_TYPE_ENTRY "alog_entry" #define MEM_TYPE_HISTORY "alog_history" #define MEM_TYPE_CURCHAN "alog_curchan" /* Last message timeout functions */ #define LASTMSG_TIMEOUT_INITIAL 5 #define LASTMSG_TIMEOUT_1(t, d) ((t) + (d) + 1 / 2) #define LASTMSG_TIMEOUT_2(t, d) ((t) + (d) + 1 / 2) #define LASTMSG_TIMEOUT_3(t) (t) #define LASTMSG_REPEATED_FMT "last message repeated %u times" /* Misc defs */ #define SYSLOG_PORT 514 /* syslogd udp port */ #define MAX_NAME 64 /* max identifier length */ #define EST_ENTRY_SIZE 100 /* estimated avg. size per entry */ enum alog_type { ALOG_NONE = 0, /* not initialized */ ALOG_NULL, /* log to nowhere */ ALOG_STDERR, /* log to stderr */ ALOG_SYSLOG_LOCAL, /* log to local machine syslog */ ALOG_SYSLOG_REMOTE /* log to remote machine syslog */ }; /* Current alog channel (thread-specific variable) */ struct alog_curchan { int channel; }; /* Stolen from syslog.h */ struct nameval { const char *name; int val; }; /* 'Last message' state for a channel */ struct alog_lastmsg { char *msg; /* previously logged message */ int sev; /* severity for 'msg' */ time_t when; /* timestamp for 'msg' */ u_int repeat; /* number of unlogged repeats */ u_int timeout; /* timeout for logging repeat */ time_t timer_expiry; /* timer running & expiry */ }; /* Structure describing one channel */ struct alog_channel { char name[MAX_NAME]; /* local syslog(3) identifier */ struct logfile *logfile; /* history logfile, or NULL */ enum alog_type type; /* channel configuration type */ int sock; /* remote syslog(3) socket */ int facility; /* channel facility */ int min_severity; /* min channel severity */ u_char debug; /* debug enabled flag */ pthread_mutex_t mutex; /* mutex, if not ALOG_NONE */ struct alog_lastmsg last; /* 'last message' info */ }; /* * Internal variables */ static struct alog_channel alog_channels[ALOG_MAX_CHANNELS]; static pthread_key_t alog_current_channel; /* Stolen from syslog.h */ static struct nameval prioritynames[] = { { "alert", LOG_ALERT, }, { "crit", LOG_CRIT, }, { "debug", LOG_DEBUG, }, { "emerg", LOG_EMERG, }, { "err", LOG_ERR, }, { "error", LOG_ERR, }, /* DEPRECATED */ { "info", LOG_INFO, }, { "notice", LOG_NOTICE, }, { "panic", LOG_EMERG, }, /* DEPRECATED */ { "warn", LOG_WARNING, }, /* DEPRECATED */ { "warning", LOG_WARNING, }, { NULL, -1, } }; /* Stolen from syslog.h */ static struct nameval facilitynames[] = { { "auth", LOG_AUTH, }, { "authpriv", LOG_AUTHPRIV, }, { "cron", LOG_CRON, }, { "daemon", LOG_DAEMON, }, { "ftp", LOG_FTP, }, { "kern", LOG_KERN, }, { "lpr", LOG_LPR, }, { "mail", LOG_MAIL, }, { "news", LOG_NEWS, }, #ifdef LOG_NTP { "ntp", LOG_NTP, }, #endif #ifdef LOG_SECURITY { "security", LOG_SECURITY, }, #endif { "syslog", LOG_SYSLOG, }, { "user", LOG_USER, }, { "uucp", LOG_UUCP, }, { "local0", LOG_LOCAL0, }, { "local1", LOG_LOCAL1, }, { "local2", LOG_LOCAL2, }, { "local3", LOG_LOCAL3, }, { "local4", LOG_LOCAL4, }, { "local5", LOG_LOCAL5, }, { "local6", LOG_LOCAL6, }, { "local7", LOG_LOCAL7, }, { NULL, -1, } }; /* * Internal functions */ static void alog_write(struct alog_channel *ch, int sev, time_t when, const char *msg); static void alog_flush_lastmsg(struct alog_channel *ch, u_int timeout); static void alog_last_check(struct alog_channel *ch); static int alog_get_channel(void); static struct alog_curchan *alog_get_current_channel(int create); static void alog_init_current_channel(void); static void alog_curchan_destroy(void *arg); /* * Initialize or reconfigure a logging channel. */ int alog_configure(int channel, const struct alog_config *conf) { struct alog_channel *const ch = &alog_channels[channel]; struct sockaddr_in sin; int init_mutex = 0; int esave; int debug; /* Sanity check */ if (channel < 0 || channel >= ALOG_MAX_CHANNELS) { errno = EINVAL; return (-1); } /* If already initialized, shut it down */ if (ch->type != ALOG_NONE) alog_shutdown(channel); /* Initialize channel */ debug = ch->debug; memset(ch, 0, sizeof(*ch)); ch->sock = -1; ch->debug = debug; if (conf->name != NULL) strlcpy(ch->name, conf->name, sizeof(ch->name)); ch->facility = (ch->debug || conf->facility == NULL) ? -1 : alog_facility(conf->facility); ch->min_severity = conf->min_severity; /* Open logfile */ if (conf->histlen > 0) { if ((ch->logfile = logfile_open(conf->path, 0, conf->histlen, conf->histlen * EST_ENTRY_SIZE)) == NULL) goto fail; } /* Initialize mutex */ if ((errno = pthread_mutex_init(&ch->mutex, NULL)) != 0) goto fail; init_mutex = 1; /* Handle stderr case */ if (ch->facility == -1) { ch->type = ALOG_STDERR; return (0); } /* Handle NULL case */ if (conf->name == NULL) { ch->type = ALOG_NULL; return (0); } /* Handle local syslog case */ if (conf->remote_server.s_addr == 0) { ch->type = ALOG_SYSLOG_LOCAL; return (0); } /* Handle remote syslog case */ if ((ch->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) goto fail; (void)fcntl(ch->sock, F_SETFD, 1); if (shutdown(ch->sock, SHUT_RD) == -1) goto fail; memset(&sin, 0, sizeof(sin)); #ifndef __linux__ sin.sin_len = sizeof(sin); #endif sin.sin_family = AF_INET; sin.sin_port = htons(SYSLOG_PORT); sin.sin_addr = conf->remote_server; if (connect(ch->sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) goto fail; ch->type = ALOG_SYSLOG_REMOTE; return (0); fail: /* Clean up after failure */ esave = errno; logfile_close(&ch->logfile); /* ok if null */ if (ch->sock != -1) { (void)close(ch->sock); ch->sock = -1; } if (init_mutex) pthread_mutex_destroy(&ch->mutex); errno = esave; return (-1); } /* * Shutdown one logging channel. */ int alog_shutdown(int channel) { struct alog_channel *const ch = &alog_channels[channel]; /* Sanity check */ if (channel < 0 || channel >= ALOG_MAX_CHANNELS) { errno = EINVAL; return (-1); } /* Cancel timer */ ch->last.timer_expiry = 0; /* Flush any repeated message */ alog_flush_lastmsg(ch, 0); /* Shut down channel */ switch (ch->type) { case ALOG_SYSLOG_REMOTE: (void)close(ch->sock); ch->sock = -1; pthread_mutex_destroy(&ch->mutex); break; case ALOG_SYSLOG_LOCAL: closelog(); pthread_mutex_destroy(&ch->mutex); break; case ALOG_NULL: case ALOG_STDERR: pthread_mutex_destroy(&ch->mutex); break; case ALOG_NONE: break; } ch->type = ALOG_NONE; /* Free saved last message */ FREE(MEM_TYPE, ch->last.msg); ch->last.msg = NULL; /* Close logfile */ logfile_close(&ch->logfile); /* ok if null */ /* Done */ return (0); } /* * Get current channel. */ static int alog_get_channel(void) { struct alog_curchan *cc; if ((cc = alog_get_current_channel(0)) == NULL) return (0); return (cc->channel); } /* * Set active channel. */ int alog_set_channel(int channel) { struct alog_curchan *cc; /* Sanity check */ if (channel < 0 || channel >= ALOG_MAX_CHANNELS) { errno = EINVAL; return (-1); } /* Compare with current channel */ if ((cc = alog_get_current_channel(channel != 0)) == NULL) { if (channel != 0) return (-1); return (0); } if (channel == cc->channel) return (0); /* Set to new channel, even if it's uninitialized */ cc->channel = channel; /* Done */ return (0); } /* * Get current channel per-thread variable. */ static struct alog_curchan * alog_get_current_channel(int create) { static pthread_once_t init_channel_once = PTHREAD_ONCE_INIT; struct alog_curchan *cc; /* Initialize per-thread variable (once) */ if ((errno = pthread_once(&init_channel_once, alog_init_current_channel)) != 0) { fprintf(stderr, "%s: %s: %s\n", "alog", "pthread_once", strerror(errno)); return (NULL); } /* Get instance for this thread; create if necessary */ if ((cc = pthread_getspecific(alog_current_channel)) == NULL) { if (!create) return (NULL); if ((cc = MALLOC(MEM_TYPE_CURCHAN, sizeof(*cc))) == NULL) { fprintf(stderr, "%s: %s: %s\n", "alog", "malloc", strerror(errno)); return (NULL); } cc->channel = 0; if ((errno = pthread_setspecific(alog_current_channel, cc)) != 0) { fprintf(stderr, "%s: %s: %s\n", "alog", "pthread_setspecific", strerror(errno)); FREE(MEM_TYPE_CURCHAN, cc); return (NULL); } } /* Return current channel */ return (cc); } /* * Initialize "current channel" per-thread variable. */ static void alog_init_current_channel(void) { int err; if ((err = pthread_key_create(&alog_current_channel, alog_curchan_destroy)) != 0) { fprintf(stderr, "%s: %s: %s\n", "alog", "pthread_key_create", strerror(err)); assert(0); } } /* * Destroy an instance of the per-thread current channel variable. */ static void alog_curchan_destroy(void *arg) { struct alog_curchan *const cc = arg; FREE(MEM_TYPE_CURCHAN, cc); } /* * Enable debugging on a channel. */ void alog_set_debug(int channel, int enabled) { struct alog_channel *const ch = &alog_channels[channel]; ch->debug = !!enabled; } /* * Log something to the currently selected channel. Preserves errno. */ void alog(int sev, const char *fmt, ...) { va_list args; va_start(args, fmt); valog(sev, fmt, args); va_end(args); } /* * Log something to the currently selected channel. Preserves errno. */ void valog(int sev, const char *fmt, va_list args) { const int errno_save = errno; struct alog_channel *const ch = &alog_channels[alog_get_channel()]; char fmtbuf[1024]; time_t now; char *msg; int r; /* Lock channel */ if (ch->type != ALOG_NONE) { r = pthread_mutex_lock(&ch->mutex); assert(r == 0); } /* Check last message timer */ alog_last_check(ch); /* Filter out unwanted verbosity */ if (ch->type != ALOG_NONE && sev > ch->min_severity) goto done; /* Allow for things like ``alog(LOG_DEBUG + 1, ...)'' */ if (sev >= LOG_DEBUG) sev = LOG_DEBUG; /* Build the message */ alog_expand(fmt, errno_save, fmtbuf, sizeof(fmtbuf)); VASPRINTF(MEM_TYPE, &msg, fmtbuf, args); if (msg == NULL) { fprintf(stderr, "%s: %s: %s\n", __FUNCTION__, "vasprintf", strerror(errno)); goto done; } /* Get current time */ now = time(NULL); /* If not configured, don't do the repeated messages thing */ if (ch->type == ALOG_NONE) { alog_write(ch, sev, now, msg); FREE(MEM_TYPE, msg); goto done; } /* Handle repeated messages */ if (ch->last.msg == NULL || strcmp(msg, ch->last.msg) != 0) { alog_flush_lastmsg(ch, LASTMSG_TIMEOUT_INITIAL); FREE(MEM_TYPE, ch->last.msg); ch->last.msg = msg; ch->last.sev = sev; ch->last.when = now; alog_write(ch, sev, now, msg); } else { time_t delay; delay = MAX(now - ch->last.when, 0); if (++ch->last.repeat == 1) { ch->last.timeout = LASTMSG_TIMEOUT_1( ch->last.timeout, delay); } else { ch->last.timeout = LASTMSG_TIMEOUT_2( ch->last.timeout, delay); } ch->last.when = now; ch->last.timer_expiry = now + ch->last.timeout; FREE(MEM_TYPE, msg); } done: /* Unlock channel and restore errno */ if (ch->type != ALOG_NONE) { r = pthread_mutex_unlock(&ch->mutex); assert(r == 0); } errno = errno_save; } /* * Check for expiration of 'last mesasge' timer. * * This assumes the channel is locked. */ static void alog_last_check(struct alog_channel *ch) { /* Is the timer "running"? */ if (ch->last.timer_expiry == 0) return; /* Has the timer "expired"? */ if (ch->last.timer_expiry > time(NULL)) return; /* "Stop" timer */ ch->last.timer_expiry = 0; /* Flush last message */ alog_flush_lastmsg(ch, LASTMSG_TIMEOUT_3(ch->last.timeout)); } /* * Log repeated message(s). * * This assumes the channel is locked. */ static void alog_flush_lastmsg(struct alog_channel *ch, u_int timeout) { switch (ch->last.repeat) { case 0: break; case 1: if (ch->last.msg != NULL) { alog_write(ch, ch->last.sev, ch->last.when, ch->last.msg); } break; default: { char buf[sizeof(LASTMSG_REPEATED_FMT) + 32]; snprintf(buf, sizeof(buf), LASTMSG_REPEATED_FMT, ch->last.repeat); alog_write(ch, ch->last.sev, ch->last.when, buf); break; } } ch->last.repeat = 0; ch->last.timeout = timeout; ch->last.timer_expiry = 0; } /* * Write a message to the log. * * This assumes the channel is locked. */ static void alog_write(struct alog_channel *ch, int sev, time_t when, const char *msg) { /* Log it to wherever */ switch (ch->type) { case ALOG_NONE: case ALOG_STDERR: fprintf(stderr, "%s: %s\n", alog_severity_name(LOG_PRI(sev)), msg); break; case ALOG_NULL: break; case ALOG_SYSLOG_LOCAL: openlog(ch->name, 0, ch->facility); /* XXX race condition */ syslog(sev, "%s", msg); /* XXX race condition */ break; case ALOG_SYSLOG_REMOTE: { char ascbuf[32]; struct tm tm; char *netmsg; int len; if (ch->sock == -1) break; gmtime_r(&when, &tm); asctime_r(&tm, ascbuf); len = ASPRINTF(TYPED_MEM_TEMP, &netmsg, "<%d>%.15s %s", LOG_MAKEPRI(ch->facility, sev), ascbuf + 4, msg); if (netmsg != NULL) write(ch->sock, netmsg, len); FREE(TYPED_MEM_TEMP, netmsg); break; } } /* Save message in history buffer */ if (ch->logfile != NULL) { struct alog_entry *ent; int mlen; /* Setup entry */ mlen = strlen(msg) + 1; if ((ent = MALLOC(MEM_TYPE, sizeof(*ent) + mlen)) == NULL) { fprintf(stderr, "%s: %s: %s\n", __FUNCTION__, "malloc", strerror(errno)); return; } ent->when = when; ent->sev = LOG_PRI(sev); memcpy(ent->msg, msg, mlen); /* Add to history file */ if (logfile_put(ch->logfile, ent, sizeof(*ent) + mlen) == -1) { fprintf(stderr, "%s: %s: %s\n", __FUNCTION__, "logfile_put", strerror(errno)); } FREE(MEM_TYPE, ent); } } /* * Get channel history */ int alog_get_history(int channel, int min_sev, int max_num, time_t max_age, const regex_t *preg, struct alog_history *list) { struct alog_channel *const ch = &alog_channels[channel]; struct alog_entry *copy; regmatch_t pmatch; u_int num_ent; int rtn = -1; time_t now; int i, j; u_int num; int r; /* Sanity check */ if (channel < 0 || channel >= ALOG_MAX_CHANNELS) { errno = EINVAL; return (-1); } /* Lock channel */ if (ch->type != ALOG_NONE) { r = pthread_mutex_lock(&ch->mutex); assert(r == 0); } /* Check last message timer */ alog_last_check(ch); /* Initialize array */ memset(list, 0, sizeof(*list)); /* Nothing to return? */ if (ch->logfile == NULL || max_num == 0 || (num_ent = logfile_num_entries(ch->logfile)) == 0) { rtn = 0; goto done; } /* Get current time */ now = time(NULL); #define MATCH(sev, when, msg, msglen) \ (sev <= min_sev && (now - when) <= max_age && (preg == NULL \ || (pmatch.rm_so = 0, pmatch.rm_eo = msglen, \ regexec(preg, msg, 0, &pmatch, REG_STARTEND) == 0))) /* Count how many entries we'll be returning */ num = 0; if (ch->last.repeat > 0 && ch->last.msg != NULL && MATCH(ch->last.sev, ch->last.when, ch->last.msg, strlen(ch->last.msg))) num++; for (i = -1; num < max_num && i >= -num_ent; i--) { const struct alog_entry *ent; int len; /* Get entry, check severity and age */ if ((ent = logfile_get(ch->logfile, i, &len)) != NULL && len >= sizeof(*ent) + 1 && MATCH(ent->sev, ent->when, ent->msg, len - sizeof(*ent) - 1)) num++; } if (num == 0) { rtn = 0; goto done; } /* Allocate array of pointers */ if ((list->elems = MALLOC(MEM_TYPE_HISTORY, num * sizeof(*list->elems))) == NULL) goto done; /* Fill array, starting with the most recent first */ j = num; if (ch->last.repeat > 0 && ch->last.msg != NULL && MATCH(ch->last.sev, ch->last.when, ch->last.msg, strlen(ch->last.msg))) { char buf[sizeof(LASTMSG_REPEATED_FMT) + 32]; const char *s; if (ch->last.repeat > 1) { snprintf(buf, sizeof(buf), LASTMSG_REPEATED_FMT, ch->last.repeat); s = buf; } else s = ch->last.msg; if ((copy = MALLOC(MEM_TYPE_ENTRY, sizeof(*copy) + strlen(s) + 1)) != NULL) { copy->when = ch->last.when; copy->sev = ch->last.sev; strcpy(copy->msg, s); list->elems[--j] = copy; } } for (i = -1; j > 0 && i >= -num_ent; i--) { const struct alog_entry *ent; int len; /* Copy it if it matches */ if ((ent = logfile_get(ch->logfile, i, &len)) != NULL && len >= sizeof(*ent) + 1 && MATCH(ent->sev, ent->when, ent->msg, len - sizeof(*ent) - 1)) { if ((copy = MALLOC(MEM_TYPE_ENTRY, len)) != NULL) { memcpy(copy, ent, len - 1); ((char *)copy)[len - 1] = '\0'; /* safety */ list->elems[--j] = copy; } } } /* Collapse entries dropped because of memory problems or whatever */ if (j > 0) { num -= j; memcpy(list->elems, list->elems + j, num * sizeof(*list->elems)); } list->length = num; rtn = 0; done: /* Unlock channel and return */ if (ch->type != ALOG_NONE) { r = pthread_mutex_unlock(&ch->mutex); assert(r == 0); } return (rtn); } /* * Clear log history. */ int alog_clear_history(int channel) { struct alog_channel *const ch = &alog_channels[channel]; int r; /* Sanity check */ if (channel < 0 || channel >= ALOG_MAX_CHANNELS) { errno = EINVAL; return (-1); } /* Lock channel */ if (ch->type != ALOG_NONE) { r = pthread_mutex_lock(&ch->mutex); assert(r == 0); } /* Reset last message state */ ch->last.timer_expiry = 0; FREE(MEM_TYPE, ch->last.msg); memset(&ch->last, 0, sizeof(ch->last)); /* Zero out logfile */ if (ch->logfile != NULL) logfile_trim(ch->logfile, 0); /* Unlock channel */ if (ch->type != ALOG_NONE) { r = pthread_mutex_unlock(&ch->mutex); assert(r == 0); } /* Done */ return (0); } /* * Decode syslog facility name */ int alog_facility(const char *name) { int i; for (i = 0; facilitynames[i].name != NULL; i++) { if (strcmp(name, facilitynames[i].name) == 0) return (facilitynames[i].val); } return (-1); } /* * Decode syslog facility name */ const char * alog_facility_name(int facility) { int i; for (i = 0; facilitynames[i].name != NULL; i++) { if (facilitynames[i].val == facility) return (facilitynames[i].name); } return (NULL); } /* * Decode syslog severity name */ int alog_severity(const char *name) { char *eptr; int i; for (i = 0; prioritynames[i].name != NULL; i++) { if (strcmp(name, prioritynames[i].name) == 0) return (prioritynames[i].val); } i = (int)strtol(name, &eptr, 0); if (*name != '\0' && *eptr == '\0') return (i); return (-1); } /* * Decode syslog severity name */ const char * alog_severity_name(int severity) { int i; for (i = 0; prioritynames[i].name != NULL; i++) { if (prioritynames[i].val == severity) return (prioritynames[i].name); } return (NULL); } /* * Kludge to deal with %m format when not using syslog */ void alog_expand(const char *fmt, int errnum, char *buf, size_t bufsize) { const char *errstr; const char *s; int errlen; int i; /* Find "%m" which is usually at the end */ for (s = fmt + strlen(fmt) - 2; s >= fmt && !(s[0] == '%' && s[1] == 'm'); s--); /* Check if we should bother doing anything */ if (s < fmt || (i = s - fmt) > bufsize - 2) { strlcpy(buf, fmt, bufsize); return; } /* Convert "%m" to error string */ errstr = strerror(errnum); errlen = strlen(errstr); strlcpy(buf, fmt, i + 1); strlcpy(buf + i, errstr, bufsize - i); strlcpy(buf + i + errlen, fmt + i + 2, bufsize - i - errlen); } /********************************************************************* STRUCTS TYPE DEFINITIONS *********************************************************************/ /* Type for "path" and "name" strings in struct alog_config */ static const struct structs_type alog_path_type = STRUCTS_STRING_TYPE("alog_config.path", 1); static const struct structs_type alog_name_type = STRUCTS_STRING_TYPE("alog_config.name", 1); /* Fields list for struct alog_config */ static const struct structs_field alog_config_fields[] = { STRUCTS_STRUCT_FIELD(alog_config, path, &alog_path_type), STRUCTS_STRUCT_FIELD(alog_config, name, &alog_name_type), STRUCTS_STRUCT_FIELD(alog_config, facility, &alog_facility_type), STRUCTS_STRUCT_FIELD(alog_config, remote_server, &structs_type_ip4), STRUCTS_STRUCT_FIELD(alog_config, min_severity, &alog_severity_type), STRUCTS_STRUCT_FIELD(alog_config, histlen, &structs_type_int), STRUCTS_STRUCT_FIELD_END }; /* Type for struct alog_config */ const struct structs_type alog_config_type = STRUCTS_STRUCT_TYPE(alog_config, &alog_config_fields); /* Override method for alog_facility_type */ static structs_init_t alog_facility_type_init; static structs_binify_t alog_facility_type_binify; /* Type for syslog facility, which we store as a string. */ const struct structs_type alog_facility_type = { sizeof(char *), "alog", STRUCTS_TYPE_PRIMITIVE, alog_facility_type_init, structs_ascii_copy, structs_string_equal, structs_string_ascify, alog_facility_type_binify, structs_string_encode, structs_string_decode, structs_string_free, { { (void *)"alog_config.facility" }, { (void *)1 } } }; /* * Initializer for alog_facility_type */ static int alog_facility_type_init(const struct structs_type *type, void *data) { return (structs_string_binify(type, "daemon", data, NULL, 0)); } /* * Binifier for alog_facility_type */ static int alog_facility_type_binify(const struct structs_type *type, const char *ascii, void *data, char *ebuf, size_t emax) { if (*ascii != '\0' && alog_facility(ascii) == -1) { strlcpy(ebuf, "invalid syslog facility", emax); errno = EINVAL; return (-1); } return (structs_string_binify(type, ascii, data, NULL, 0)); } /* Override methods for alog_severity_type */ static structs_init_t alog_severity_init; static structs_ascify_t alog_severity_ascify; static structs_binify_t alog_severity_binify; /* Type for severity, which defaults to LOG_INFO. The input can be either a symbolic name or a numeric value. */ const struct structs_type alog_severity_type = { sizeof(int), "int", STRUCTS_TYPE_PRIMITIVE, alog_severity_init, structs_region_copy, structs_region_equal, alog_severity_ascify, alog_severity_binify, structs_region_encode_netorder, structs_region_decode_netorder, structs_nothing_free, { { (void *)2 }, { (void *)1 } } }; /* * Initializer for severity */ static int alog_severity_init(const struct structs_type *type, void *data) { *((int *)data) = LOG_INFO; return (0); } /* * Ascifier for severity */ static char * alog_severity_ascify(const struct structs_type *type, const char *mtype, const void *data) { const int sev = *((int *)data); const char *s; char buf[32]; if ((s = alog_severity_name(sev)) == NULL) { snprintf(buf, sizeof(buf), "%d", sev); s = buf; } return (STRDUP(mtype, s)); } /* * Binifier for severity */ static int alog_severity_binify(const struct structs_type *type, const char *ascii, void *data, char *ebuf, size_t emax) { int sev; if ((sev = alog_severity(ascii)) == -1 && structs_int_binify(type, ascii, data, NULL, 0) == -1) { strlcpy(ebuf, "invalid syslog severity", emax); return (-1); } *((int *)data) = sev; return (0); } /* * Ascifier for log entry message. */ static char * alog_entry_msg_ascify(const struct structs_type *type, const char *mtype, const void *data) { return (STRDUP(mtype, data)); } /* Type for log entry message. This is a "read only" type. */ static const struct structs_type alog_entry_msg_type = { 0, "alog_entry_msg_type", STRUCTS_TYPE_PRIMITIVE, structs_notsupp_init, structs_notsupp_copy, structs_notsupp_equal, alog_entry_msg_ascify, structs_notsupp_binify, structs_notsupp_encode, structs_notsupp_decode, structs_nothing_free, }; /* Type for history list: an array of pointers to log entries */ static const struct structs_field alog_entry_fields[] = { STRUCTS_STRUCT_FIELD(alog_entry, when, &structs_type_time_abs), STRUCTS_STRUCT_FIELD(alog_entry, sev, &alog_severity_type), STRUCTS_STRUCT_FIELD(alog_entry, msg, &alog_entry_msg_type), STRUCTS_STRUCT_FIELD_END }; static const struct structs_type alog_entry_type = STRUCTS_STRUCT_TYPE(alog_entry, &alog_entry_fields); static const struct structs_type alog_history_ptr_type = STRUCTS_POINTER_TYPE(&alog_entry_type, MEM_TYPE_ENTRY); const struct structs_type alog_history_type = STRUCTS_ARRAY_TYPE(&alog_history_ptr_type, MEM_TYPE_HISTORY, "entry");