/**************************************************************************/ // dawnlog.cpp - dawn logging implementation /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with the dawn license * * in licenses.txt... In particular, you may not remove this copyright * * notice. * **************************************************************************/ #include "include.h" #ifdef unix #include <sys/types.h> #include <sys/socket.h> #include <signal.h> #endif #ifdef WIN32 #include <process.h> #endif /**************************************************************************/ extern int hotreboot_ipc_pipe; /**************************************************************************/ char initial_startup_log_buffer[4*MSL+1]; bool initial_startup_log_buffer_full=false; bool log_hold_log_string_core_stdout_restore_value; bool log_string_core_stdout_enabled=true; #ifdef WIN32 bool commandlineoption_no_background_process=true; // --nobackground commandline switches this bool commandlineoption_log_console=true; #else bool commandlineoption_no_background_process=false; // --nobackground commandline switches this bool commandlineoption_log_console=false; #endif bool commandlineoption_no_logfile=false; /**************************************************************************/ // returns the time in the format 2 digit year, month, day (6 digits total) char *dawnlog_format_year_month_day(time_t time_to_format) { static char result[MSL]; char buf[MSL]; // year month day strftime(buf, MSL, "%Y%m%d", localtime( &time_to_format)); // %Y is used instead of %y to avoid y2k compiler warnings strcpy(result, &buf[2]); // skip the leading 2 digits of the year return result; } /**************************************************************************/ // returns the start of the next day time_t dawnlog_get_tomorrow_start() { // find out our time currently struct tm now; time_t tommorrow_start; now= *localtime( ¤t_time); now.tm_sec=0; // seconds now.tm_min=0; // minutes now.tm_hour=0; // hours now.tm_mday++; // day of the month - jumping to tommorrow //now.tm_mon; // month //now.tm_year; // year //now.tm_wday; // day of the week //now.tm_yday; // day in the year //now.tm_isdst; // daylight saving time tommorrow_start=mktime(&now); // // Testing Code // logf("Tommorrow is %s", ctime(&tommorrow_start)); // logf("Tommorrow formatted is %s", dawnlog_format_year_month_day(tommorrow_start)); // logf("Now is %s", ctime(¤t_time)); // logf("Now formatted is %s", dawnlog_format_year_month_day(current_time)); return tommorrow_start; } /**************************************************************************/ time_t last_current_time=0; FILE *current_logfile_descriptor=NULL; char current_logfile_name[MSL]; time_t rotate_logfilename_after=0; /**************************************************************************/ FILE *dawnlog_get_new_logfile(int portprefix, char *logfilename) { // get a date code for today FILE *newfile; char today[MSL]; char possible_filename[MSL]; strcpy(today, dawnlog_format_year_month_day(current_time)); int i; for(i=1; i<4000; i++){ sprintf(possible_filename, "%s%d-%s-%02d.log", GAME_LOGS_DIR, portprefix, today, i); if(!file_exists(possible_filename)){ break; }; } if(i==4000){ fprintf(stderr, "Failed to find a free filename for logging in " "the format %s after 4000 attempts! - aborting!\n", possible_filename); fflush(stderr); fprintf(stdout, "Failed to find a free filename for logging in " "the format %s after 4000 attempts! - aborting!\n", possible_filename); fflush(stdout); do_abort(); } newfile=fopen(possible_filename, "w"); if(!newfile){ char buf[MSL]; sprintf(buf, "dawnlog_get_new_logfile(): fopen '%s' failed for write - error %d (%s) - aborting!\n", possible_filename, errno, strerror( errno)); fprintf(stderr, "%s", buf); fflush(stderr); fprintf(stdout, "%s", buf); fflush(stdout); do_abort(); } strcpy(logfilename, possible_filename); return newfile; } /**************************************************************************/ // all logging to stdout goes thru here, and should only be called // from within log_string_core static void log_string_core_stdout(const char *str ) { if(log_string_core_stdout_enabled){ fprintf( stdout, "%s", str); // in log_string_core_stdout } } /**************************************************************************/ void log_hold_till_commandline_options_parsed() { log_hold_log_string_core_stdout_restore_value=log_string_core_stdout_enabled; log_string_core_stdout_enabled=false; } /**************************************************************************/ void log_release_held_logs() { log_string_core_stdout_enabled=log_hold_log_string_core_stdout_restore_value; log_string_core_stdout(initial_startup_log_buffer); } /**************************************************************************/ void dawnlog_write_index(char *str) { append_string_to_file( DAWNLOG_INDEX_FILE, FORMATF("[%d] %s :: %s", getpid(), shortdate(NULL), str), true); } /**************************************************************************/ void display_host_info(); void netio_binded_listen_on_output(); extern char **main_argv; /**************************************************************************/ // all logging should ultimately come thru here void log_string_core(const char *str ) { if(!mainport){ log_string_core_stdout(str); // until we have the main port, we don't try and allocate a logging file // but we do record the log information to initial_startup_log_buffer if(!initial_startup_log_buffer_full && str_len(initial_startup_log_buffer)+str_len(str)<4*MSL) { strcat(initial_startup_log_buffer, str); }else{ initial_startup_log_buffer_full=true; logf("initial_startup_log_buffer exceeded, some logging information will be lost."); } return; } if(commandlineoption_no_logfile){ log_string_core_stdout(str); return; } // rotate our logfiles daily if(current_logfile_descriptor && rotate_logfilename_after<current_time){ FILE *new_logfile_descriptor; char new_logfile_name[MSL]; // start by calculating the start of tommorrow in time_t format // - this is done first so we can continue to use the log functions // without recursively executing this particular subsection of // log_string_core() rotate_logfilename_after=dawnlog_get_tomorrow_start(); new_logfile_descriptor=dawnlog_get_new_logfile(mainport, new_logfile_name); logf("transfering logging from %s", current_logfile_name); logf("information about the current process in these logs:"); display_host_info(); netio_binded_listen_on_output(); logf("logging continues in %s", new_logfile_name); fclose(current_logfile_descriptor); current_logfile_descriptor=new_logfile_descriptor; new_logfile_descriptor=NULL; logf("logging continuation of %s", current_logfile_name); logf("information about the current process:"); display_host_info(); netio_binded_listen_on_output(); strcpy(current_logfile_name, new_logfile_name); new_logfile_name[0]='\0'; } if(!current_logfile_descriptor){ // the mud has just booted up, and mainport has just come live int portprefix=mainport; mainport=0; log_string("initiating logging to file"); // open the log file current_logfile_descriptor=dawnlog_get_new_logfile(portprefix, current_logfile_name); // calculate the time we will want to write to the log file we just opened until rotate_logfilename_after=dawnlog_get_tomorrow_start(); mainport=portprefix; if(current_logfile_descriptor){ log_string_core(initial_startup_log_buffer); initial_startup_log_buffer[0]='\0'; if(initial_startup_log_buffer_full){ logf("initial_startup_log_buffer exceeded, some logging information will be lost."); initial_startup_log_buffer_full=false; } logf("logging to %s", current_logfile_name); dawnlog_write_index("============================================"); dawnlog_write_index(FORMATF("dawn logging to %s",current_logfile_name)); log_notef("note: on machines which have the tail command installed, " "you can follow this log using the command: tail -n 5000 -f %s", current_logfile_name); log_notef("note2: you can change the startup behaviour if desired, to do so" "`1 type \"%s --help\" for more information.", main_argv[0]); #ifdef unix if(commandlineoption_no_background_process){ logf("Mud remaining in foreground, process id = %d.", getpid()); }else{ // switch to become a daemon logf("switching to daemon mode (background running process)"); pid_t fork_result=fork(); // check if something went wrong if(fork_result<0){ bugf("switching to background daemon failed! errno=%d (%s)", errno, strerror(errno)); }else if(fork_result){ // parent... close it off, so they can continue on with the shell sleep_seconds(1); exit_clean(0, "log_string_core", "parent process closing down after going background"); } if(setsid() < 0){ // shoudn't fail bugf("failed to set new session id."); } logf("The mud is now running in the background, process id = %d.", getpid()); } #endif if(!commandlineoption_log_console){ log_string_core_stdout_enabled=false; } } } log_string_core_stdout(str); if(current_logfile_descriptor){ int slen=str_len(str); if(fwrite(str, slen, 1, current_logfile_descriptor)!=1){ fprintf(stderr, "error %d (%s) writing text to %s\n", errno, strerror( errno), current_logfile_name); fprintf(stderr, "%s", str); fflush(stderr); } } } /**************************************************************************/ void log_string_flush() { fflush(stdout); if(current_logfile_descriptor){ fflush(current_logfile_descriptor); } } /**************************************************************************/ // Writes a string to the log. void log_string( const char *str ) { if(runlevel==RUNLEVEL_INIT){ return; } char ipc_num[20]; if(hotreboot_ipc_pipe>=0){ sprintf(ipc_num, "%d", hotreboot_ipc_pipe); }else{ ipc_num[0]=0; } if(game_settings && GAMESETTING2(GAMESET2_VERBOSE_DATES_IN_LOGS)){ last_current_time=0; } if(last_current_time==current_time){ log_string_core( FORMATF(":%s: ", ipc_num)); }else{ log_string_core( FORMATF("[%d]%s:%s: ", getpid(), shortdate(NULL)+4, ipc_num)); } log_string_core( str ); // this is not included in the FORMATF above to // avoid having to worry about the size of buffers log_string_core( "\n" ); last_current_time=current_time; log_string_flush(); return; } /**************************************************************************/ void fulltime_log_string( const char *str ) { if(runlevel==RUNLEVEL_INIT){ return; } log_string_core( FORMATF("%s :: ", shortdate(NULL))); log_string_core( str ); log_string_core( "\n" ); log_string_flush(); return; } /**************************************************************************/ // draw a line of *'s in the logs on a line without the time void log_bar( ) { last_current_time=current_time; log_string("**************************************************************************"); } /**************************************************************************/ // put a note in the log void log_note( const char *text) { char buf[MSL*4]; strcpy(buf, "*************************************************************************\n"); char *msg=str_dup(text); msg=note_format_string_width(msg, 77, false, true); msg=string_replace_all(msg,"`1","\n"); strcat(buf, msg); free_string(msg); strcat(buf, "****************************************************************************"); last_current_time=current_time; log_string(buf); } /**************************************************************************/ void log_notef(char * fmt, ...) { char buf[MSL*4]; va_list args; va_start(args, fmt); vsnprintf(buf, MSL*4-MIL, fmt, args); va_end(args); log_note(buf); } /**************************************************************************/ /**************************************************************************/ /**************************************************************************/