/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* Portions Copyright 2010 by Rainer Gerhards and Adiscon */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "rsyslog.h" #include "imklog.h" /* TODO: this define should be changed over time to the more generic * system-provided (configurable) upper limit. However, it is quite * unexpected that Solaris-emitted messages are so long, so it seems * acceptable to set a fixed (relatively high) limit for the time * being -- and gain some experience with it. -- rgerhars, 2010-04-12 */ #define MAXLINE 4096 static struct pollfd Pfd; /* Pollfd for local the log device */ /* findnl_bkwd: * Scans each character in buf until it finds the last newline in buf, * or the scanned character becomes the last COMPLETE character in buf. * Returns the number of scanned bytes. * * buf - pointer to a buffer containing the message string * len - the length of the buffer */ size_t findnl_bkwd(const char *buf, const size_t len) { const char *p; size_t mb_cur_max; if (len == 0) { return (0); } mb_cur_max = MB_CUR_MAX; if (mb_cur_max == 1) { /* single-byte locale */ for (p = buf + len - 1; p != buf; p--) { if (*p == '\n') { return ((size_t)(p - buf)); } } return ((size_t)len); } else { /* multi-byte locale */ int mlen; const char *nl; size_t rem; p = buf; nl = NULL; for (rem = len; rem >= mb_cur_max; ) { mlen = mblen(p, mb_cur_max); if (mlen == -1) { /* * Invalid character found. */ dbgprintf("klog:findnl_bkwd: Invalid MB sequence\n"); /* * handle as a single byte character. */ p++; rem--; } else { /* * It's guaranteed that *p points to * the 1st byte of a multibyte character. */ if (*p == '\n') { nl = p; } p += mlen; rem -= mlen; } } if (nl) { return ((size_t)(nl - buf)); } /* * no newline nor null byte found. * Also it's guaranteed that *p points to * the 1st byte of a (multibyte) character * at this point. */ return (len - rem); } } //___ end /* Attempts to open the local log device * and return a file descriptor. */ int sun_openklog(char *name, int mode) { int fd; struct strioctl str; if ((fd = open(name, mode)) < 0) { dbgprintf("klog:openklog: cannot open %s (%d)\n", name, errno); return (-1); } str.ic_cmd = I_CONSLOG; str.ic_timout = 0; str.ic_len = 0; str.ic_dp = NULL; if (ioctl(fd, I_STR, &str) < 0) { dbgprintf("klog:openklog: cannot register to log " "console messages (%d)\n", errno); return (-1); } Pfd.fd = fd; Pfd.events = POLLIN; return (fd); } /* * Pull up one message from log driver. */ void sun_getkmsg() { int flags = 0, i; char *lastline; struct strbuf ctl, dat; struct log_ctl hdr; char buf[MAXLINE+1]; size_t buflen; size_t len; char tmpbuf[MAXLINE+1]; dat.maxlen = MAXLINE; dat.buf = buf; ctl.maxlen = sizeof (struct log_ctl); ctl.buf = (caddr_t)&hdr; while ((i = getmsg(Pfd.fd, &ctl, &dat, &flags)) == MOREDATA) { lastline = &dat.buf[dat.len]; *lastline = '\0'; dbgprintf("klog:sys_poll: getmsg: dat.len = %d\n", dat.len); buflen = strlen(buf); len = findnl_bkwd(buf, buflen); (void) memcpy(tmpbuf, buf, len); tmpbuf[len] = '\0'; Syslog(LOG_INFO, (uchar*) buf); if (len != buflen) { /* If anything remains in buf */ size_t remlen; if (buf[len] == '\n') { /* skip newline */ len++; } /* Move the remaining bytes to * the beginnning of buf. */ remlen = buflen - len; (void) memcpy(buf, &buf[len], remlen); dat.maxlen = MAXLINE - remlen; dat.buf = &buf[remlen]; } else { dat.maxlen = MAXLINE; dat.buf = buf; } } if (i == 0 && dat.len > 0) { dat.buf[dat.len] = '\0'; /* Format sys will enqueue the log message. * Set the sync flag if timeout != 0, which * means that we're done handling all the * initial messages ready during startup. */ dbgprintf("klog:getkmsg: getmsg: dat.maxlen = %d\n", dat.maxlen); dbgprintf("klog:getkmsg: getmsg: dat.len = %d\n", dat.len); dbgprintf("klog:getkmsg: getmsg: strlen(dat.buf) = %d\n", strlen(dat.buf)); dbgprintf("klog:getkmsg: getmsg: dat.buf = \"%s\"\n", dat.buf); dbgprintf("klog:getkmsg: buf len = %d\n", strlen(buf)); Syslog(LOG_INFO, (uchar*) buf); } else if (i < 0 && errno != EINTR) { if(1){ /* V5-TODO: rsyslog-like termination! (!shutting_down) { */ dbgprintf("klog:kernel log driver read error"); } // TODO trigger retry logic //(void) close(Pfd.fd); //Pfd.fd = -1; } } /* this thread listens to the local stream log driver for log messages * generated by this host, formats them, and queues them to the logger * thread. */ /*ARGSUSED*/ void * sun_sys_poll() { int nfds; dbgprintf("klog:sys_poll: sys_thread started\n"); for (;;) { errno = 0; nfds = poll(&Pfd, 1, INFTIM); if (nfds == 0) continue; if (nfds < 0) { if (errno != EINTR) dbgprintf("klog:poll error"); continue; } if (Pfd.revents & POLLIN) { sun_getkmsg(); } else { /* TODO: shutdown, the rsyslog way (in v5!) -- check shutdown flag */ if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) { // TODO: trigger retry logic /* logerror("kernel log driver poll error"); (void) close(Pfd.fd); Pfd.fd = -1; */ } } } /*NOTREACHED*/ return (NULL); }