// Copyright (C) 1999-2002 Silicon Graphics, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or modify it // under the terms of version 2 of the GNU General Public License as // published by the Free Software Foundation. // // This program is distributed in the hope that it would be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any // license provided herein, whether implied or otherwise, is limited to // this program in accordance with the express provisions of the GNU // General Public License. Patent licenses, if any, provided herein do not // apply to combinations of this program with other product or programs, or // any other product whatsoever. This program is distributed without any // warranty that the program is delivered free of the rightful claim of any // third person by way of infringement or the like. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write the Free Software Foundation, Inc., 59 // Temple Place - Suite 330, Boston MA 02111-1307, USA. #include "Log.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_AUDIT #include #endif Log::LogLevel Log::level = ERROR; bool Log::log_to_stderr = false; const char *Log::program_name = "fam"; bool Log::syslog_open = false; unsigned Log::count = 0; #ifdef HAVE_AUDIT bool Log::audit_enabled = true; #endif void Log::debug() { level = DEBUG; info("log level is LOG_DEBUG"); } void Log::info() { level = INFO; info("log level is LOG_INFO"); } void Log::error() { level = INFO; info("log level is LOG_ERR"); level = ERROR; } void Log::disable_audit() { #ifdef HAVE_AUDIT audit_enabled = false; info("disabling security audit trail"); #endif } void Log::foreground() { log_to_stderr = true; if (syslog_open) { closelog(); syslog_open = false; } } void Log::background() { log_to_stderr = false; } void Log::name(const char *newname) { program_name = newname; audit(true, "fam changing process name to \"%s\"", newname); } void Log::debug(const char *fmt, ...) { if (level >= DEBUG) { va_list a; va_start(a, fmt); vlog(DEBUG, fmt, a); va_end(a); } } void Log::info(const char *fmt, ...) { if (level >= INFO) { va_list a; va_start(a, fmt); vlog(INFO, fmt, a); va_end(a); } } void Log::error(const char *fmt, ...) { if (level >= ERROR) { va_list a; va_start(a, fmt); vlog(ERROR, fmt, a); va_end(a); } } void Log::critical(const char *fmt, ...) { if (level >= CRITICAL) { va_list a; va_start(a, fmt); vlog(CRITICAL, fmt, a); va_end(a); } } void Log::perror(const char *format, ...) { if (level >= ERROR) { char * buf = new char[strlen(format) + 5]; (void) strcpy(buf, format); (void) strcat(buf, ": %m"); va_list args; va_start(args, format); vlog(ERROR, buf, args); va_end(args); delete[] buf; } } void Log::audit(bool success, const char *format, ...) { #ifdef HAVE_AUDIT if (audit_enabled) { // This is not so good. If there were a version of satwrite // which took a va_list, this buffer would not need to be here. char buf[SAT_MAX_USER_REC]; va_list args; va_start(args, format); int len = vsnprintf(buf, SAT_MAX_USER_REC, format, args); va_end(args); if(len > 0) satwrite(SAT_AE_CUSTOM, success ? SAT_SUCCESS : SAT_FAILURE, buf, len); } #endif } // This is like debug(), info(), & error(), but it logs regardless of the // current log level. void Log::log(LogLevel l, const char *fmt, ...) { va_list a; va_start(a, fmt); vlog(l, fmt, a); va_end(a); } void Log::vlog(LogLevel l, const char *format, va_list args) { if (log_to_stderr) vfglog(format, args); else { if (!syslog_open) { openlog(program_name, LOG_PID, LOG_DAEMON); syslog_open = true; } int ll; switch (l) { case DEBUG: ll = LOG_DEBUG; break; case INFO: ll = LOG_INFO; break; case ERROR: ll = LOG_ERR; break; case CRITICAL: default: ll = LOG_CRIT; break; } vsyslog(ll, format, args); } } void Log::vfglog(const char *format, va_list args) { // Count number of %'s in the format string. That's the max // number of %m's that there can be. int numPercents = 0; const char *pt = format; while(*pt) { if (*pt++ == '%') numPercents++; } char * err; // Only get the error string if there's a chance we'll use it. if (numPercents > 0) { err = strerror(errno); if (err == NULL) { err = "Unknown error"; } } else { err = ""; } // This 2 is for the \n and the null terminator added by the strcpy. char *buf = new char[strlen(format) + 2 + strlen(err) * numPercents]; char *p = buf; while (*format) { if (format[0] == '%' && format[1] == 'm') { p += strlen(strcpy(p, err)); format += 2; } else { *p++ = *format++; } } (void) strcpy(p, "\n"); (void) fprintf(stderr, "%s[%d]: ", program_name, getpid()); (void) vfprintf(stderr, buf, args); delete[] buf; } #ifndef NDEBUG // New back end for assert() will log to syslog, put core file // in known directory. void __assert(const char *msg, const char *file, int line) { char *dirname = new char[strlen(Log::getName()) + 20]; (void) sprintf(dirname, "/usr/tmp/%s.%d", Log::getName(), getpid()); Log::error("Assertion failed at %s line %d: %s", file, line, msg); Log::error("Dumping core in %s/core", dirname); if (setreuid(0, 0) < 0) Log::perror("setreuid"); if (mkdir(dirname, 0755) < 0) Log::perror("mkdir"); if (chdir(dirname) < 0) Log::perror("chdir"); struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if (setrlimit(RLIMIT_CORE, &rl) < 0) Log::perror("setrlimit(RLIMIT_CORE)"); delete[] dirname; abort(); } #endif /* !NDEBUG */ #ifdef HAPPY_PURIFY Log::Log() { count++; } Log::~Log() { if (!--count) { if (syslog_open) { closelog(); syslog_open = false; } } } #endif /* HAPPY_PURIFY */ #ifdef UNIT_TEST_Log #include #include int main() { Log::name("unit test"); Log::debug(); Log::foreground(); Log::debug("De bug is in %s.", "de rug"); Log::info("Thank %s for sharing %s.", "you", "that"); Log::error("The %s in %s falls.", "rain", "Spain"); if (open("/foo%bar", 0) < 0) Log::perror("/foo%c%s", '%', "bar"); if (chmod("/", 0777) < 0) Log::error("%m on chmod(\"%s\", 0%o)", "/", 0777); return 0; } #endif /* UNIT_TEST_Log */