#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; uint32_t maxkB = 10240; string OUTPUTFILE = ""; int PROGRAM_ARGV_IDX = -1; bool ECHO_TO_STDOUT = false; bool DELETE_EXISTING_LOG_FILE = false; bool PRINT_COMMAND_STR = false; int MAX_SPAWNS = 1; int SECS_BETWEEN_SPAWNS = 15; bool CHILD_SIGNALED = false; bool ADD_TIMESTAMP_PREFIX = true; string COMMAND_STR; // string of command to execute (used just for printing) pid_t CHILD_PID = 0; int WAIT_RETURN_STATUS = 1; rlim_t COREDUMPSIZE = 0; void RunProcess(int narg, char *argv[], int spawn_attempt); string GetChildResourceUsageString(void); void TruncateOutputFile(ofstream &ofs, bool make_headroom=true); void ParseCommandLineArguments(int narg, char *argv[]); void Usage(bool quit); //..................... // sigHandler void sigHandler(int sigNum) { if(CHILD_PID != 0){ CHILD_SIGNALED = true; kill(CHILD_PID, sigNum); }else{ // Should never get here since child will have replaced this process if(sigNum != SIGCHLD) exit(0); } } //..................... // waitThread void* waitThread(void *arg) { // thread that will sit in // "wait" so we can capture // resource usage of child while(true){ wait(&WAIT_RETURN_STATUS); // Keep waiting until child is terminated if(WIFEXITED(WAIT_RETURN_STATUS)) break; if(WIFSIGNALED(WAIT_RETURN_STATUS)) break; //cout << "STOP OR CONTINUE RECEIVED!!" << endl; } pthread_exit(NULL); } //----------------------------- // main //----------------------------- int main(int narg, char *argv[]) { ParseCommandLineArguments(narg, argv); cout <<"-------------hdlog starting--------------" << endl; cout <<" command: " << COMMAND_STR << endl; cout <<" max. file size : " << maxkB << "kB" << endl; cout <<" log file name: " << OUTPUTFILE << endl; cout <<" log mode: " << (DELETE_EXISTING_LOG_FILE ? "overwrite existing":"append") << endl; cout <<" max. respawns: " << MAX_SPAWNS << endl; // Loop as many times as we should respawn the program. for(int i=0; i 0 ){ // Set soft limit to smaller of COREDUMPSIZE or // current max limit. Keep current max limit. struct rlimit new_limit = { COREDUMPSIZE, save_limit.rlim_max }; if( COREDUMPSIZE < new_limit.rlim_max ) new_limit.rlim_cur = new_limit.rlim_max; cout <<"Setting coredumpsize to " << COREDUMPSIZE << " (was " << save_limit.rlim_cur << ")" << endl; setrlimit(RLIMIT_CORE, &new_limit); } // Fork child process pid_t pid = fork(); if(pid == -1){ cerr << "Error forking !!!" << endl; exit(-2); } if(pid == 0){ // ------- child close(fd_stdout[0]); close(fd_stderr[0]); // Set both stdout and stderr to feed the fd_stdout pipe dup2(fd_stdout[1], STDOUT_FILENO); dup2(fd_stdout[1], STDERR_FILENO); execvp(argv[PROGRAM_ARGV_IDX], &argv[PROGRAM_ARGV_IDX]); }else{ // ------- parent close(fd_stdout[1]); close(fd_stderr[1]); // Dispatch signals to child process CHILD_PID = pid; signal(SIGINT, sigHandler); signal(SIGTERM, sigHandler); // Reset coredump file size for parent (if set to something else above) if( COREDUMPSIZE > 0 ){ setrlimit(RLIMIT_CORE, &save_limit); } // Launch a thread to wait on the child process and // capture resource usage for it pthread_t thread; pthread_create(&thread, NULL, waitThread, NULL); ios_base::openmode mode = ofstream::app; if(DELETE_EXISTING_LOG_FILE) mode = ofstream::out; ofstream ofs(OUTPUTFILE.c_str(), mode); time_t t = time(NULL); string tstr(ctime(&t)); tstr.erase(tstr.length()-1); ofs << endl; ofs << "------- hdlog starting: " << tstr << " ----------" << endl; if(PRINT_COMMAND_STR) ofs << "command: " << COMMAND_STR << endl << endl; // Record if we had to respawn child if( spawn_attempt > 0 ){ cout << "--------- respawning " << spawn_attempt+1 << "/" << MAX_SPAWNS << "--------------" << endl; ofs << "respawning: " << spawn_attempt+1 << "/" << MAX_SPAWNS << endl; } uint32_t buff_size = 16386; char *buff = new char[buff_size]; long filesize = ofs.tellp(); // Loop until done do{ ssize_t Nbytes = read(fd_stdout[0], buff, buff_size); if(Nbytes<=0) break; string prefix = ""; if(ADD_TIMESTAMP_PREFIX){ t = time(NULL); prefix = ctime(&t); prefix.erase(prefix.length()-1); prefix = string("[") + prefix + "] "; } if(ECHO_TO_STDOUT){ if( !prefix.empty() ) cout << prefix; cout.write(buff, Nbytes); cout.flush(); } if( !prefix.empty() ) ofs << prefix; ofs.write(buff, Nbytes); ofs.flush(); filesize += Nbytes; if(filesize/1024 > maxkB){ TruncateOutputFile(ofs); filesize = ofs.tellp(); // get new file size } }while(true); delete[] buff; t = time(NULL); tstr = ctime(&t); tstr.erase(tstr.length()-1); ofs << endl; ofs << "------- hdlog ending: " << tstr << " ----------" << endl; void *ptr; pthread_join(thread, &ptr); // Final check on max file size filesize = ofs.tellp(); if(filesize/1024 > maxkB){ TruncateOutputFile(ofs, false); } filesize = ofs.tellp(); cout <<" final log file size : " << filesize/1024 << "kB" << endl; // Get child resource usage string usage_str = GetChildResourceUsageString(); ofs << usage_str; cout << usage_str; // Print info on respawning ofs << " CHILD_SIGNALED = " << CHILD_SIGNALED <= narg){ cout << endl << "You must provide the name of a program to run!" << endl; Usage(true); } // Record start of actual command user wants us to run PROGRAM_ARGV_IDX = iarg; // Optionally generate output file name if(OUTPUTFILE == ""){ const char *host = getenv("HOST"); const char *logdir = getenv("HDLOG"); string shost = (host==NULL ? "":host); size_t pos = shost.find_first_of("."); if(pos != shost.npos) shost.erase(pos); string slogdir = (logdir==NULL ? "":logdir); if(slogdir != "") slogdir += "/"; OUTPUTFILE = slogdir + argv[iarg] + "_" + shost + ".log"; } // Create string of command line we're going to execute for(int i=PROGRAM_ARGV_IDX; i