/*PRINTFLIKE4*/ static void syslog_emit(fmd_hdl_t *hdl, char *buf, size_t len, const char *msgformat, ...) { struct strbuf ctl, dat; uint32_t msgid; char *format; size_t formatlen; va_list ap; formatlen = strlen(msgformat) + 64; /* +64 for prefix and \0 */ format = alloca(formatlen); STRLOG_MAKE_MSGID(msgformat, msgid); (void) snprintf(format, formatlen, "fmd: [ID %u FACILITY_AND_PRIORITY] %s", msgid, msgformat); va_start(ap, msgformat); (void) vsnprintf(buf, len, format, ap); va_end(ap); ctl.buf = (void *)&syslog_ctl; ctl.len = sizeof (syslog_ctl); dat.buf = buf; dat.len = strlen(buf) + 1; if (syslog_file && putmsg(syslog_logfd, &ctl, &dat, 0) != 0) { fmd_hdl_debug(hdl, "putmsg failed: %s\n", strerror(errno)); syslog_stats.log_err.fmds_value.ui64++; } dat.buf = strchr(buf, ']'); dat.len -= (size_t)(dat.buf - buf); dat.buf[0] = '\r'; /* overwrite ']' with carriage return */ dat.buf[1] = '\n'; /* overwrite ' ' with newline */ if (syslog_cons && write(syslog_msgfd, dat.buf, dat.len) != dat.len) { fmd_hdl_debug(hdl, "write failed: %s\n", strerror(errno)); syslog_stats.msg_err.fmds_value.ui64++; } }
void vsyslog(int pri, const char *fmt, va_list ap) { char *b, *f, *o; char c; int clen; char buf[MAXLINE + 2]; char outline[MAXLINE + 256]; /* pad to allow date, system name... */ time_t now; pid_t pid; struct log_ctl hdr; struct strbuf dat; struct strbuf ctl; char timestr[26]; /* hardwired value 26 due to Posix */ size_t taglen; int olderrno = errno; struct stat statbuff; int procfd; char procfile[32]; psinfo_t p; int showpid; uint32_t msgid; char *msgid_start, *msgid_end; int nowait; /* * Maximum tag length is 256 (the pad in outline) minus the size of the * other things that can go in the pad. */ #define MAX_TAG 230 /* see if we should just throw out this message */ if (pri < 0 || PRIFAC(pri) >= LOG_NFACILITIES || (PRIMASK(pri) & LogMask) == 0) return; if (LogFileInvalid) return; /* * if openlog() has not been called by the application, * try to get the name of the application and set it * as the ident string for messages. If unable to get * it for any reason, fall back to using the default * of syslog. If we succeed in getting the name, also * turn on LOG_PID, to provide greater detail. */ showpid = 0; if (OpenLogCalled == 0) { (void) sprintf(procfile, "/proc/%d/psinfo", (int)getpid()); if ((procfd = open(procfile, O_RDONLY)) >= 0) { if (read(procfd, &p, sizeof (psinfo_t)) >= 0) { (void) strncpy(ProcName, p.pr_fname, PRFNSZ); LogTag = (const char *) &ProcName; showpid = LOG_PID; } (void) close(procfd); } } if (LogFile < 0) openlog(LogTag, LogStat|LOG_NDELAY|showpid, 0); if ((fstat(LogFile, &statbuff) != 0) || (!S_ISCHR(statbuff.st_mode)) || (statbuff.st_rdev != LogDev)) { LogFileInvalid = TRUE; return; } /* set default facility if none specified */ if ((pri & LOG_FACMASK) == 0) pri |= LogFacility; /* build the header */ hdr.pri = pri; hdr.flags = SL_CONSOLE; hdr.level = 0; /* build the message */ /* * To avoid potential security problems, bounds checking is done * on outline and buf. * The following code presumes that the header information will * fit in 250-odd bytes, as was accounted for in the buffer size * allocation. This is dependent on the assumption that the LogTag * and the string returned by sprintf() for getpid() will return * be less than 230-odd characters combined. */ o = outline; (void) time(&now); (void) sprintf(o, "%.15s ", ctime_r(&now, timestr, 26) + 4); o += strlen(o); if (LogTag) { taglen = strlen(LogTag) < MAX_TAG ? strlen(LogTag) : MAX_TAG; (void) strncpy(o, LogTag, taglen); o[taglen] = '\0'; o += strlen(o); } if (LogStat & LOG_PID) { (void) sprintf(o, "[%d]", (int)getpid()); o += strlen(o); } if (LogTag) { (void) strcpy(o, ": "); o += 2; } STRLOG_MAKE_MSGID(fmt, msgid); (void) sprintf(o, "[ID %u FACILITY_AND_PRIORITY] ", msgid); o += strlen(o); b = buf; f = (char *)fmt; while ((c = *f++) != '\0' && b < &buf[MAXLINE]) { char *errmsg; if (c != '%') { *b++ = c; continue; } if ((c = *f++) != 'm') { *b++ = '%'; *b++ = c; continue; } if ((errmsg = strerror(olderrno)) == NULL) (void) snprintf(b, &buf[MAXLINE] - b, "error %d", olderrno); else { while (*errmsg != '\0' && b < &buf[MAXLINE]) { if (*errmsg == '%') { (void) strcpy(b, "%%"); b += 2; } else *b++ = *errmsg; errmsg++; } *b = '\0'; } b += strlen(b); } if (b > buf && *(b-1) != '\n') /* ensure at least one newline */ *b++ = '\n'; *b = '\0'; /* LINTED variable format specifier */ (void) vsnprintf(o, &outline[sizeof (outline)] - o, buf, ap); clen = (int)strlen(outline) + 1; /* add one for NULL byte */ if (clen > MAXLINE) { clen = MAXLINE; outline[MAXLINE-1] = '\0'; } /* * 1136432 points out that the underlying log driver actually * refuses to accept (ERANGE) messages longer than LOG_MAXPS * bytes. So it really doesn't make much sense to putmsg a * longer message.. */ if (clen > LOG_MAXPS) { clen = LOG_MAXPS; outline[LOG_MAXPS-1] = '\0'; } /* set up the strbufs */ ctl.maxlen = sizeof (struct log_ctl); ctl.len = sizeof (struct log_ctl); ctl.buf = (caddr_t)&hdr; dat.maxlen = sizeof (outline); dat.len = clen; dat.buf = outline; /* output the message to the local logger */ if ((putmsg(LogFile, &ctl, &dat, 0) >= 0) && syslogd_ok()) return; if (!(LogStat & LOG_CONS)) return; /* * Output the message to the console directly. To reduce visual * clutter, we strip out the message ID. */ if ((msgid_start = strstr(outline, "[ID ")) != NULL && (msgid_end = strstr(msgid_start, "] ")) != NULL) (void) strcpy(msgid_start, msgid_end + 2); clen = strlen(outline) + 1; nowait = (LogStat & LOG_NOWAIT); pid = forkx(nowait? 0 : (FORK_NOSIGCHLD | FORK_WAITPID)); if (pid == -1) return; if (pid == 0) { sigset_t sigs; int fd; (void) sigset(SIGALRM, SIG_DFL); (void) sigemptyset(&sigs); (void) sigaddset(&sigs, SIGALRM); (void) sigprocmask(SIG_UNBLOCK, &sigs, NULL); (void) alarm(5); if (((fd = open(sysmsg, O_WRONLY)) >= 0) || (fd = open(ctty, O_WRONLY)) >= 0) { (void) alarm(0); outline[clen - 1] = '\r'; (void) write(fd, outline, clen); (void) close(fd); } _exit(0); } if (!nowait) while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) continue; }
static void pr_vsyslog(int sockfd, int pri, register const char *fmt, va_list ap) { time_t now; static char logbuf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; size_t buflen = 0; int len = 0, saved_errno = errno; #ifdef HAVE_DEV_LOG_STREAMS struct strbuf ctl, dat; struct log_ctl lc; #else char *timestr = NULL; # ifdef HAVE_TZNAME char *saved_tzname[2]; # endif /* HAVE_TZNAME */ #endif /* Clear the buffer */ memset(logbuf, '\0', sizeof(logbuf)); /* Check for invalid bits. */ if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { pri &= LOG_PRIMASK|LOG_FACMASK; } /* Check priority against setlogmask values. */ if ((LOG_MASK(pri & LOG_PRIMASK) & log_mask) == 0) { return; } /* Set default facility if none specified. */ if ((pri & LOG_FACMASK) == 0) { pri |= log_facility; } #ifndef HAVE_DEV_LOG_STREAMS len = snprintf(logbuf, sizeof(logbuf), "<%d>", pri); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; # ifdef HAVE_TZNAME /* Preserve the old tzname setting. */ memcpy(saved_tzname, tzname, sizeof(saved_tzname)); # endif /* HAVE_TZNAME */ time(&now); timestr = ctime(&now); # ifdef HAVE_TZNAME /* Restore the old tzname setting, to prevent ctime(3) from inadvertently * affecting things, as when we're in a chroot, and ctime(3) loses the * timezone info. */ memcpy(tzname, saved_tzname, sizeof(saved_tzname)); # endif /* HAVE_TZNAME */ /* Remove the trailing newline from the time string returned by ctime(3). */ timestr[strlen(timestr)-1] = '\0'; /* Skip past the leading "day of week" prefix. */ timestr += 4; len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "%.15s ", timestr); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; #endif time(&now); if (log_ident == NULL) { #ifdef HAVE___PROGNAME log_ident = __progname; #else log_ident = "proftpd"; #endif /* HAVE___PROGNAME */ } if (buflen < sizeof(logbuf) && log_ident != NULL) { len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "%s", log_ident); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; } if (buflen < sizeof(logbuf)-1 && (log_opts & LOG_PID)) { len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "[%d]", (int) getpid()); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; } if (buflen < sizeof(logbuf)-1 && log_ident != NULL) { len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, ": "); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; } #if defined(SOLARIS2_9) || defined(SOLARIS2_10) /* Add in the (IMHO stupid and nonportable) syslog "header" that was added * to the Solaris 9/10 libc syslog(3) function. Some sites apparently * think that trying to use this header to generate reports of logging * is a Good Idea; I'll have the last laugh when those sites try to move * to a different platform with different syslog logging. * * The header to be added looks like: * * "[ID %lu %s.%s]" * * where the ID is generated using STRLOG_MAKE_MSGID(), a macro defined * in <sys/strlog.h>, and the following two strings are the syslog * facility and level, respectively. */ if (buflen < sizeof(logbuf)) { register unsigned int i; uint32_t msgid; const char *facility_name = "unknown", *level_name = "unknown"; STRLOG_MAKE_MSGID(fmt, msgid); for (i = 0; syslog_facility_names[i].name; i++) { if (syslog_facility_names[i].facility == log_facility) { facility_name = syslog_facility_names[i].name; break; } } for (i = 0; syslog_level_names[i].name; i++) { if (syslog_level_names[i].level == (pri & LOG_PRIMASK)) { level_name = syslog_level_names[i].name; break; } } len = snprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, "[ID %lu %s.%s] ", (unsigned long) msgid, facility_name, level_name); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; } #endif /* Solaris 9 or 10 */ /* Restore errno for %m format. */ errno = saved_errno; /* We have the header. Print the user's format into the buffer. */ if (buflen < sizeof(logbuf)) { len = vsnprintf(&(logbuf[buflen]), sizeof(logbuf) - buflen, fmt, ap); logbuf[sizeof(logbuf)-1] = '\0'; buflen += len; } /* Always make sure the buffer is NUL-terminated */ logbuf[sizeof(logbuf)-1] = '\0'; /* If we have a SOCK_STREAM connection, also send ASCII NUL as a record * terminator. */ if (sock_type == SOCK_STREAM) { ++buflen; } /* If we have exceeded the capacity of the buffer, we're done here. */ if (buflen >= sizeof(logbuf)) { return; } #ifndef HAVE_DEV_LOG_STREAMS if (sockfd >= 0 && send(sockfd, logbuf, buflen, 0) < 0) { fprintf(stderr, "error sending log message '%s' to socket fd %d: %s\n", logbuf, sockfd, strerror(errno)); } #else /* Prepare the structs for use by putmsg(). As /dev/log (or /dev/conslog) * is a STREAMS device on Solaris (and possibly other platforms?), putmsg() is * used so that syslog facility and level are properly honored; write() * does not seem to work as desired. */ ctl.len = ctl.maxlen = sizeof(lc); ctl.buf = (char *) &lc; dat.len = dat.maxlen = buflen; dat.buf = logbuf; lc.level = 0; lc.flags = SL_CONSOLE; lc.pri = pri; putmsg(sockfd, &ctl, &dat, 0); #endif }
/* * Ideally we would just use syslog(3C) for outputting our messages, but our * messaging standard defines a nice multi-line format and syslogd(1M) is very * inflexible and stupid when it comes to multi-line messages. It pulls data * out of log(7D) and splits it up by \n, printing each line to the console * with its usual prefix of date and sender; it uses the same behavior for the * messages file as well. Further, syslog(3C) provides no CE_CONT equivalent * for userland callers (which at least works around repeated file prefixing). * So with a multi-line message format, your file and console end up like this: * * Dec 02 18:08:40 hostname this is my nicely formatted * Dec 02 18:08:40 hostname message designed for 80 cols * ... * * To resolve these issues, we use our own syslog_emit() wrapper to emit * messages and some knowledge of how the Solaris log drivers work. We first * construct an enlarged format string containing the appropriate msgid(1). * We then format the caller's message using the provided format and buffer. * We send this message to log(7D) using putmsg() with SL_CONSOLE | SL_LOGONLY * set in the log_ctl_t. The log driver allows us to set SL_LOGONLY when we * construct messages ourself, indicating that syslogd should only emit the * message to /var/adm/messages and any remote hosts, and skip the console. * Then we emit the message a second time, without the special prefix, to the * sysmsg(7D) device, which handles console redirection and also permits us * to output any characters we like to the console, including \n and \r. */ static void syslog_emit(fmd_hdl_t *hdl, const char *msg) { struct strbuf ctl, dat; uint32_t msgid; char *buf; size_t buflen; const char *format = "fmd: [ID %u FACILITY_AND_PRIORITY] %s"; STRLOG_MAKE_MSGID(format, msgid); buflen = snprintf(NULL, 0, format, msgid, msg); buf = alloca(buflen + 1); (void) snprintf(buf, buflen + 1, format, msgid, msg); ctl.buf = (void *)&syslog_ctl; ctl.len = sizeof (syslog_ctl); dat.buf = buf; dat.len = buflen + 1; /* * The underlying log driver won't accept messages longer than * LOG_MAXPS bytes. Therefore, messages which exceed this limit will * be truncated and appended with a pointer to the full message. */ if (dat.len > LOG_MAXPS) { char *syslog_pointer, *p; size_t plen; if ((syslog_pointer = fmd_msg_gettext_id(syslog_msghdl, NULL, SYSLOG_POINTER)) == NULL) { /* * This shouldn't happen, but if it does we'll just * truncate the message. */ buf[LOG_MAXPS - 1] = '\0'; dat.len = LOG_MAXPS; } else { plen = strlen(syslog_pointer) + 1; buf[LOG_MAXPS - plen] = '\0'; /* * If possible, the pointer is appended after a newline */ if ((p = strrchr(buf, '\n')) == NULL) p = &buf[LOG_MAXPS - plen]; (void) strcpy(p, syslog_pointer); free(syslog_pointer); dat.len = strlen(buf) + 1; } } if (syslog_file && putmsg(syslog_logfd, &ctl, &dat, 0) != 0) { fmd_hdl_debug(hdl, "putmsg failed: %s\n", strerror(errno)); syslog_stats.log_err.fmds_value.ui64++; } dat.buf = strchr(buf, ']'); dat.len -= (size_t)(dat.buf - buf); dat.buf[0] = '\r'; /* overwrite ']' with carriage return */ dat.buf[1] = '\n'; /* overwrite ' ' with newline */ if (syslog_cons && write(syslog_msgfd, dat.buf, dat.len) != dat.len) { fmd_hdl_debug(hdl, "write failed: %s\n", strerror(errno)); syslog_stats.msg_err.fmds_value.ui64++; } }