/* * log.c - logging routines */ #include "copyright.h" #include "config.h" #include <sys/types.h> #include <sys/time.h> #include <time.h> #include "db.h" #include "mudconf.h" #include "externs.h" #include "flags.h" #include "powers.h" #include "alloc.h" #include "htab.h" #include "ansi.h" #ifdef ARBITRARY_LOGFILES #include "logcache.h" #endif NAMETAB logdata_nametab[] = { {(char *) "flags", 1, 0, LOGOPT_FLAGS}, {(char *) "location", 1, 0, LOGOPT_LOC}, {(char *) "owner", 1, 0, LOGOPT_OWNER}, {(char *) "timestamp", 1, 0, LOGOPT_TIMESTAMP}, {NULL, 0, 0, 0} }; NAMETAB logoptions_nametab[] = { {(char *) "accounting", 2, 0, LOG_ACCOUNTING}, {(char *) "all_commands", 2, 0, LOG_ALLCOMMANDS}, {(char *) "suspect_commands", 2, 0, LOG_SUSPECTCMDS}, {(char *) "bad_commands", 2, 0, LOG_BADCOMMANDS}, {(char *) "buffer_alloc", 3, 0, LOG_ALLOCATE}, {(char *) "bugs", 3, 0, LOG_BUGS}, {(char *) "checkpoints", 2, 0, LOG_DBSAVES}, {(char *) "config_changes", 2, 0, LOG_CONFIGMODS}, {(char *) "create", 2, 0, LOG_PCREATES}, {(char *) "killing", 1, 0, LOG_KILLS}, {(char *) "logins", 1, 0, LOG_LOGIN}, {(char *) "network", 1, 0, LOG_NET}, {(char *) "problems", 1, 0, LOG_PROBLEMS}, {(char *) "security", 2, 0, LOG_SECURITY}, {(char *) "shouts", 2, 0, LOG_SHOUTS}, {(char *) "startup", 2, 0, LOG_STARTUP}, {(char *) "wizard", 1, 0, LOG_WIZARD}, {NULL, 0, 0, 0} }; char *strip_ansi_r(char *dest, char *raw, size_t n) { char *p = (char *) raw; char *q = dest; while (p && *p && ((q - dest) < n)) { if(*p == ESC_CHAR) { /* * Start of ANSI code. Skip to end. */ while (*p && !isalpha(*p)) p++; if(*p) p++; } else *q++ = *p++; } *q = '\0'; return dest; } char *strip_ansi(const char *raw) { static char buf[LBUF_SIZE]; char *p = (char *) raw; char *q = buf; while (p && *p) { if(*p == ESC_CHAR) { /* * Start of ANSI code. Skip to end. */ while (*p && !isalpha(*p)) p++; if(*p) p++; } else *q++ = *p++; } *q = '\0'; return buf; } char *normal_to_white(const char *raw) { static char buf[LBUF_SIZE]; char *p = (char *) raw; char *q = buf; while (p && *p) { if(*p == ESC_CHAR) { /* * Start of ANSI code. */ *q++ = *p++; /* * ESC CHAR */ *q++ = *p++; /* * [ character. */ if(*p == '0') { /* * ANSI_NORMAL */ safe_str("0m", buf, &q); safe_chr(ESC_CHAR, buf, &q); safe_str("[37m", buf, &q); p += 2; } } else *q++ = *p++; } *q = '\0'; return buf; } /** * See if it's is OK to log something, and if so, start writing the * log entry. */ int start_log(const char *primary, const char *secondary) { struct tm *tp; time_t now; mudstate.logging++; switch (mudstate.logging) { case 1: case 2: /* * Format the timestamp */ if((mudconf.log_info & LOGOPT_TIMESTAMP) != 0) { time((time_t *) (&now)); tp = localtime((time_t *) (&now)); sprintf(mudstate.buffer, "%d%02d%02d.%02d%02d%02d ", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); } else { mudstate.buffer[0] = '\0'; } /* * Write the header to the log */ if(secondary && *secondary) fprintf(stderr, "%s%s %3s/%-5s: ", mudstate.buffer, mudconf.mud_name, primary, secondary); else fprintf(stderr, "%s%s %-9s: ", mudstate.buffer, mudconf.mud_name, primary); /* * If a recursive call, log it and return indicating no log */ if(mudstate.logging == 1) return 1; fprintf(stderr, "Recursive logging request.\r\n"); default: mudstate.logging--; } return 0; } /** * Finish up writing a log entry */ void end_log(void) { fprintf(stderr, "\n"); fflush(stderr); mudstate.logging--; } /** * Write perror message to the log */ void log_perror(const char *primary, const char *secondary, const char *extra, const char *failing_object) { start_log(primary, secondary); if(extra && *extra) { log_text((char *) "("); log_text((char *) extra); log_text((char *) ") "); } perror((char *) failing_object); fflush(stderr); mudstate.logging--; } /** * Write text to log file. */ void log_text(char *text) { fprintf(stderr, "%s", strip_ansi(text)); } void log_error(int key, char *primary, char *secondary, char *format, ...) { char buffer[LBUF_SIZE]; char stripped_buffer[LBUF_SIZE]; va_list ap; if(!(key & mudconf.log_options)) return; if(mudconf.log_info & LOGOPT_TIMESTAMP) { time_t now; struct tm tm; time(&now); localtime_r(&now, &tm); fprintf(stderr, "%d%02d%02d.%02d%02d%02d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } if(secondary && &secondary) { fprintf(stderr, "%s%s %3s/%-5s: ", mudstate.buffer, mudconf.mud_name, primary, secondary); } else { fprintf(stderr, "%s%s %-9s: ", mudstate.buffer, mudconf.mud_name, primary); } va_start(ap, format); vsnprintf(buffer, LBUF_SIZE, format, ap); va_end(ap); strip_ansi_r(stripped_buffer, buffer, LBUF_SIZE); fprintf(stderr, "%s\n", stripped_buffer); } void log_printf(char *format, ...) { char buffer[LBUF_SIZE]; char stripped_buffer[LBUF_SIZE]; va_list ap; va_start(ap, format); vsnprintf(buffer, LBUF_SIZE, format, ap); va_end(ap); strip_ansi_r(stripped_buffer, buffer, LBUF_SIZE); fprintf(stderr, "%s\n", stripped_buffer); } /* * Write a number to log file. */ void log_number(int num) { fprintf(stderr, "%d", num); } /** * Writes the name, db number, and flags of an object to the log. * If the object does not own itself, append the name, db number, and flags * of the owner. */ void log_name(dbref target) { char *tp; if((mudconf.log_info & LOGOPT_FLAGS) != 0) tp = unparse_object((dbref) GOD, target, 0); else tp = unparse_object_numonly(target); fprintf(stderr, "%s", strip_ansi(tp)); free_lbuf(tp); if(((mudconf.log_info & LOGOPT_OWNER) != 0) && (target != Owner(target))) { if((mudconf.log_info & LOGOPT_FLAGS) != 0) tp = unparse_object((dbref) GOD, Owner(target), 0); else tp = unparse_object_numonly(Owner(target)); fprintf(stderr, "[%s]", strip_ansi(tp)); free_lbuf(tp); } return; } /** * Log both the name and location of an object */ void log_name_and_loc(dbref player) { log_name(player); if((mudconf.log_info & LOGOPT_LOC) && Has_location(player)) { log_text((char *) " in "); log_name(Location(player)); } return; } /* * Returns the object type of specified object. */ char *OBJTYP(dbref thing) { if(!Good_obj(thing)) { return (char *) "??OUT-OF-RANGE??"; } switch (Typeof(thing)) { case TYPE_PLAYER: return (char *) "PLAYER"; case TYPE_THING: return (char *) "THING"; case TYPE_ROOM: return (char *) "ROOM"; case TYPE_EXIT: return (char *) "EXIT"; case TYPE_GARBAGE: return (char *) "GARBAGE"; default: return (char *) "??ILLEGAL??"; } } void log_type_and_name(dbref thing) { char nbuf[16]; log_text(OBJTYP(thing)); sprintf(nbuf, " #%d(", thing); log_text(nbuf); if(Good_obj(thing)) log_text(Name(thing)); log_text((char *) ")"); return; } void log_type_and_num(dbref thing) { char nbuf[16]; log_text(OBJTYP(thing)); sprintf(nbuf, " #%d", thing); log_text(nbuf); return; } #ifdef ARBITRARY_LOGFILES int log_to_file(dbref thing, const char *logfile, const char *message) { char pathname[210]; /* Arbitrary limit in logfile length */ char message_buffer[4096]; if(!message || !*message) return 1; /* Nothing to do */ if(!logfile || !*logfile || strlen(logfile) > 200) return 0; /* invalid logfile name */ if(strstr(pathname, "..") != NULL) return 0; if(strstr(pathname, "/") != NULL) return 0; snprintf(pathname, 210, "logs/%s", logfile); /* Hacking checks. */ if(access(pathname, R_OK | W_OK) != 0) return 0; snprintf(message_buffer, 4096, "%s\n", message); if(!logcache_writelog(pathname, message_buffer)) { notify(thing, "Serious failure while trying to write to log."); return 0; } return 1; } void do_log(dbref player, dbref cause, int key, char *logfile, char *message) { if(!message || !*message) { notify(player, "Nothing to log!"); return; } if(!logfile || !*logfile) { notify(player, "Invalid logfile."); return; } if(!log_to_file(player, logfile, message)) { notify(player, "Request failed."); return; } notify(player, "Message logged."); } #endif