// Given an offset count that points to a delimiter, this function returns the // previous delimiter offset position. // If clusterId and procId is specified, it will not return the immediately // previous delimiter, but the nearest previous delimiter that matches static long findPrevDelimiter(FILE *fd, char* filename, long currOffset) { MyString buf; char *owner; long prevOffset = -1, completionDate = -1; int clusterId = -1, procId = -1; fseek(fd, currOffset, SEEK_SET); buf.readLine(fd); owner = (char *) malloc(buf.Length() * sizeof(char)); // Current format of the delimiter: // *** ProcId = a ClusterId = b Owner = "cde" CompletionDate = f // For the moment, owner and completionDate are just parsed in, reserved for future functionalities. sscanf(buf.Value(), "%*s %*s %*s %ld %*s %*s %d %*s %*s %d %*s %*s %s %*s %*s %ld", &prevOffset, &clusterId, &procId, owner, &completionDate); if (prevOffset == -1 && clusterId == -1 && procId == -1) { fprintf(stderr, "Error: (%s) is an incompatible history file, please run condor_convert_history.\n", filename); free(owner); exit(1); } // If clusterId.procId is specified if (cluster != -1 || proc != -1) { // Ok if only clusterId specified while (clusterId != cluster || (proc != -1 && procId != proc)) { if (prevOffset == 0) { // no match free(owner); return -1; } // Find previous delimiter + summary fseek(fd, prevOffset, SEEK_SET); buf.readLine(fd); owner = (char *) realloc (owner, buf.Length() * sizeof(char)); sscanf(buf.Value(), "%*s %*s %*s %ld %*s %*s %d %*s %*s %d %*s %*s %s %*s %*s %ld", &prevOffset, &clusterId, &procId, owner, &completionDate); } } free(owner); return prevOffset; }
// This is a blocking call and must provide a fully booted partition when it // returns. Otherwise, this partition could be overcommitted given the // nature of the use of this call. // script partition_name size kind void Partition::boot(char *script, PKind pkind) { FILE *fin = NULL; ArgList args; MyString line; priv_state priv; // we're told what kind of partition this is going to be set_pkind(pkind); dprintf(D_ALWAYS, "\t%s %s %ld %s\n", script, get_name().Value(), get_size(), pkind_xlate(get_pkind()).Value()); args.AppendArg(script); args.AppendArg(get_name()); args.AppendArg(get_size()); args.AppendArg(pkind_xlate(get_pkind()).Value()); priv = set_root_priv(); fin = my_popen(args, "r", MY_POPEN_OPT_WANT_STDERR); line.readLine(fin); // read back OK or NOT_OK, XXX ignore my_pclose(fin); set_priv(priv); // Now that the script is done, mark it booted. set_pstate(BOOTED); }
bool privsep_get_switchboard_response(FILE* err_fp, MyString *response) { // first read everything off the error pipe and close // the error pipe // MyString err; while (err.readLine(err_fp, true)) { } fclose(err_fp); // if this is passed in, assume the caller will handle any // error propagation, and we just succeed. if (response) { *response = err; return true; } // if there was something there, print it out here (since no one captured // the error message) and return false to indicate something went wrong. if (err.Length() != 0) { dprintf(D_ALWAYS, "privsep_get_switchboard_response: error received: %s", err.Value()); return false; } // otherwise, indicate that everything's fine // return true; }
void Partition::back(char *script) { FILE *fin = NULL; ArgList args; MyString line; priv_state priv; dprintf(D_ALWAYS, "\t%s %s %ld %s\n", script, get_name().Value(), get_size(), pkind_xlate(get_pkind()).Value()); args.AppendArg(script); args.AppendArg(get_name()); args.AppendArg(get_size()); args.AppendArg(pkind_xlate(get_pkind()).Value()); priv = set_root_priv(); fin = my_popen(args, "r", MY_POPEN_OPT_WANT_STDERR); line.readLine(fin); // read back OK or NOT_OK, XXX ignore my_pclose(fin); set_priv(priv); // we don't know it is backed until the // STARTD_FACTORY_SCRIPT_AVAILABLE_PARTITIONS // tells us it is actually backed. This prevents overcommit of a // partition to multiple startds. set_pstate(ASSIGNED); }
// Given a history file, returns the position offset of the last delimiter // The last delimiter will be found in the last line of file, // and will start with the "***" character string static long findLastDelimiter(FILE *fd, char *filename) { int i; bool found; long seekOffset, lastOffset; MyString buf; struct stat st; // Get file size stat(filename, &st); found = false; i = 0; while (!found) { // 200 is arbitrary, but it works well in practice seekOffset = st.st_size - (++i * 200); fseek(fd, seekOffset, SEEK_SET); while (1) { if (buf.readLine(fd) == false) break; // If line starts with *** and its last line of file if (strncmp(buf.Value(), "***", 3) == 0 && buf.readLine(fd) == false) { found = true; break; } } if (seekOffset <= 0) { fprintf(stderr, "Error: Unable to find last delimiter in file: (%s)\n", filename); exit(1); } } // lastOffset = beginning of delimiter lastOffset = ftell(fd) - buf.Length(); return lastOffset; }
//! Gets the writer password required by the quill++ // daemon to access the database static MyString getWritePassword(const char *write_passwd_fname, const char *host, const char *port, const char *db, const char *dbuser) { FILE *fp = NULL; MyString passwd; int len; MyString prefix; MyString msbuf; const char *buf; bool found = FALSE; // prefix is for the prefix of the entry in the .pgpass // it is in the format of the following: // host:port:db:user:password prefix.sprintf("%s:%s:%s:%s:", host, port, db, dbuser); len = prefix.Length(); fp = safe_fopen_wrapper(write_passwd_fname, "r"); if(fp == NULL) { EXCEPT("Unable to open password file %s\n", write_passwd_fname); } //dprintf(D_ALWAYS, "prefix: %s\n", prefix); while(msbuf.readLine(fp)) { msbuf.chomp(); buf = msbuf.Value(); //fprintf(stderr, "line: %s\n", buf); // check if the entry matches the prefix if (strncmp(buf, prefix.Value(), len) == 0) { // extract the password passwd = msbuf.Substr(len, msbuf.Length()); found = TRUE; break; } } fclose(fp); if (!found) { EXCEPT("Unable to find password from file %s\n", write_passwd_fname); } return passwd; }
bool write_local_settings_from_file(FILE* out_fp, const char* param_name, const char* start_mark, const char* end_mark) { char* tmp = param(param_name); if (tmp == NULL) { return true; } MyString local_settings_file = tmp; free(tmp); if (start_mark != NULL) { if (fprintf(out_fp, "%s\n", start_mark) < 0) { vmprintf(D_ALWAYS, "fprintf error writing start marker: %s\n", strerror(errno)); return false; } } FILE* in_fp = safe_fopen_wrapper_follow(local_settings_file.Value(), "r"); if (in_fp == NULL) { vmprintf(D_ALWAYS, "fopen error on %s: %s\n", local_settings_file.Value(), strerror(errno)); return false; } MyString line; while (line.readLine(in_fp)) { if (fputs(line.Value(), out_fp) == EOF) { vmprintf(D_ALWAYS, "fputs error copying local settings: %s\n", strerror(errno)); fclose(in_fp); return false; } } fclose(in_fp); if (end_mark != NULL) { if (fprintf(out_fp, "%s\n", end_mark) == EOF) { vmprintf(D_ALWAYS, "fputs error writing end marker: %s\n", strerror(errno)); return false; } } return true; }
int GLExecPrivSepHelper::run_script(ArgList& args,MyString &error_desc) { if (!proxy_valid_right_now()) { dprintf(D_ALWAYS, "GLExecPrivSepHelper::run_script: not invoking glexec since the proxy is not valid!\n"); error_desc += "The job proxy is not valid."; return INVALID_PROXY_RC; } /* Note that set_user_priv is a no-op if condor is running as non-root (the "usual" mode for invoking glexec) */ priv_state priv_saved = set_user_priv(); FILE* fp = my_popen(args, "r", TRUE); set_priv(priv_saved); if (fp == NULL) { dprintf(D_ALWAYS, "GLExecPrivSepHelper::run_script: " "my_popen failure on %s: errno=%d (%s)\n", args.GetArg(0), errno, strerror(errno)); return -1; } MyString str; while (str.readLine(fp, true)); priv_saved = set_user_priv(); int ret = my_pclose(fp); set_priv(priv_saved); if (ret != 0) { str.trim(); dprintf(D_ALWAYS, "GLExecPrivSepHelper::run_script: %s exited " "with status %d and following output:\n%s\n", args.GetArg(0), ret, str.Value()); error_desc.formatstr_cat("%s exited with status %d and the following output: %s", condor_basename(args.GetArg(0)), ret, str.Value()); error_desc.replaceString("\n","; "); } return ret; }
//--------------------------------------------------------------------------- // Here we re-read the jobstate.log file to find out what sequence number // we should start with when running a rescue DAG. void JobstateLog::InitializeRescue() { debug_printf( DEBUG_DEBUG_2, "JobstateLog::InitializeRescue()\n" ); if ( !_jobstateLogFile ) { return; } FILE *infile = safe_fopen_wrapper_follow( _jobstateLogFile, "r" ); if ( !infile ) { // This is a fatal error, because by the time we get here, // we should, at the very least, have written the // DAGMAN_STARTED "event". debug_printf( DEBUG_QUIET, "Could not open jobstate log file %s for reading.\n", _jobstateLogFile ); main_shutdown_graceful(); return; } int maxSeqNum = 0; MyString line; while ( line.readLine( infile ) ) { time_t newTimestamp; MyString nodeName; int seqNum; if ( ParseLine( line, newTimestamp, nodeName, seqNum ) ) { maxSeqNum = MAX( maxSeqNum, seqNum ); } } fclose( infile ); debug_printf( DEBUG_DEBUG_2, "Max sequence num in jobstate.log file: %d\n", maxSeqNum ); Job::SetJobstateNextSequenceNum( maxSeqNum + 1 ); }
bool UserProc::JobReaper(int pid, int status) { MyString line; MyString error_txt; MyString filename; const char* dir = Starter->GetWorkingDir(); FILE* fp; dprintf( D_FULLDEBUG, "Inside UserProc::JobReaper()\n" ); filename.sprintf("%s%c%s", dir, DIR_DELIM_CHAR, JOB_WRAPPER_FAILURE_FILE); if (0 == access(filename.Value(), F_OK)) { // The job wrapper failed, so read the contents of the file // and EXCEPT, just as is done when an executable is unable // to be run. Ideally, both failure cases would propagate // into the job ad fp = safe_fopen_wrapper_follow(filename.Value(), "r"); if (!fp) { dprintf(D_ALWAYS, "Unable to open \"%s\" for reading: " "%s (errno %d)\n", filename.Value(), strerror(errno), errno); } else { while (line.readLine(fp)) { error_txt += line; } fclose(fp); } error_txt.trim(); EXCEPT("The job wrapper failed to execute the job: %s", error_txt.Value()); } if (JobPid == pid) { m_proc_exited = true; exit_status = status; job_exit_time.getTime(); } return m_proc_exited; }
// This is a blocking call and must provide a fully shutdown partition when it // returns. // script partition_name void Partition::shutdown(char *script) { FILE *fin = NULL; ArgList args; MyString line; priv_state priv; dprintf(D_ALWAYS, "\t%s %s\n", script, get_name().Value()); args.AppendArg(script); args.AppendArg(get_name()); priv = set_root_priv(); fin = my_popen(args, "r", MY_POPEN_OPT_WANT_STDERR); line.readLine(fin); // read back OK or NOT_OK, XXX ignore my_pclose(fin); set_priv(priv); // Now that the script is done, mark it simply generated. set_pstate(GENERATED); }
bool privsep_get_switchboard_response(FILE* err_fp) { // first read everything off the error pipe and close // the error pipe // MyString err; while (err.readLine(err_fp, true)) { } fclose(err_fp); // if there was something there, print it out and return // an indication that something went wrong // if (err.Length() != 0) { dprintf(D_ALWAYS, "privsep_get_switchboard_response: error received: %s", err.Value()); return false; } // otherwise, indicate that everything's fine // return true; }
static int check_if_docker_offline(MyPopenTimer & pgmIn, const char * cmd_str, int original_error_code) { int rval = original_error_code; // this should not be called with a program that is still running. ASSERT(pgmIn.is_closed()); MyString line; MyStringCharSource * src = NULL; if (pgmIn.output_size() > 0) { src = &pgmIn.output(); src->rewind(); } bool check_for_hung_docker = true; // if no output, we should check for hung docker. dprintf( D_ALWAYS | D_FAILURE, "%s failed, %s output.\n", cmd_str, src ? "printing first few lines of" : "no" ); if (src) { check_for_hung_docker = false; // if we got output, assume docker is not hung. for (int ii = 0; ii < 10; ++ii) { if ( ! line.readLine(*src, false)) break; dprintf( D_ALWAYS | D_FAILURE, "%s\n", line.c_str() ); // if we got something resembling "/var/run/docker.sock: resource temporarily unavaible" // then we should check for a hung docker. const char * p = strstr(line.c_str(), ".sock: resource "); if (p && strstr(p, "unavailable")) { check_for_hung_docker = true; } } } if (check_for_hung_docker) { dprintf( D_ALWAYS, "Checking to see if Docker is offline\n"); ArgList infoArgs; add_docker_arg(infoArgs); infoArgs.AppendArg( "info" ); MyString displayString; infoArgs.GetArgsStringForLogging( & displayString ); MyPopenTimer pgm2; if (pgm2.start_program(infoArgs, true, NULL, false) < 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); rval = DockerAPI::docker_hung; } else { int exitCode = 0; if ( ! pgm2.wait_for_exit(60, &exitCode) || pgm2.output_size() <= 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to get output from '%s' : %s.\n", displayString.c_str(), pgm2.error_str() ); rval = DockerAPI::docker_hung; } else { while (line.readLine(pgm2.output(),false)) { line.chomp(); dprintf( D_FULLDEBUG, "[Docker Info] %s\n", line.c_str() ); } } } if (rval == DockerAPI::docker_hung) { dprintf( D_ALWAYS | D_FAILURE, "Docker is not responding. returning docker_hung error code.\n"); } } return rval; }
ClassAd* readJobAd( void ) { ClassAd* ad = NULL; bool is_stdin = false; bool read_something = false; ASSERT( job_ad_file ); if( job_ad_file[0] == '-' && job_ad_file[1] == '\0' ) { fp = stdin; is_stdin = true; } else { if (fp == NULL) { fp = safe_fopen_wrapper_follow( job_ad_file, "r" ); if( ! fp ) { EXCEPT( "Failed to open ClassAd file (%s): %s (errno %d)", job_ad_file, strerror(errno), errno ); } } } dprintf( D_FULLDEBUG, "Reading job ClassAd from %s\n", is_stdin ? "STDIN" : job_ad_file ); ad = new ClassAd; MyString line; while( line.readLine(fp) ) { read_something = true; line.chomp(); if( line[0] == '#' ) { dprintf( D_JOB, "IGNORING COMMENT: %s\n", line.Value() ); continue; } if( line == "***" ) { dprintf( D_JOB, "Saw ClassAd delimitor, stopping\n" ); break; } if( ! ad->Insert(line.Value()) ) { EXCEPT( "Failed to insert \"%s\" into ClassAd!", line.Value() ); } } if( ! read_something ) { EXCEPT( "reading ClassAd from (%s): file is empty", is_stdin ? "STDIN" : job_ad_file ); } if( IsDebugVerbose(D_JOB) ) { ad->dPrint( D_JOB ); } // For debugging, see if there's a special attribute in the // job ad that sends us into an infinite loop, waiting for // someone to attach with a debugger int shadow_should_wait = 0; ad->LookupInteger( ATTR_SHADOW_WAIT_FOR_DEBUG, shadow_should_wait ); if( shadow_should_wait ) { dprintf( D_ALWAYS, "Job requested shadow should wait for " "debugger with %s=%d, going into infinite loop\n", ATTR_SHADOW_WAIT_FOR_DEBUG, shadow_should_wait ); while( shadow_should_wait ) { } } return ad; }
int main( int argc, char* argv[] ) { int i; param_functions *p_funcs = NULL; set_mySubSystem( "DAEMON-TOOL", SUBSYSTEM_TYPE_TOOL ); MyName = argv[0]; myDistro->Init( argc, argv ); FILE *input_fp = stdin; for( i=1; i<argc; i++ ) { if( match_prefix( argv[i], "-daemontype" ) ) { if( argv[i + 1] ) { get_mySubSystem()->setName( argv[++i] ); get_mySubSystem()->setTypeFromName( ); } else { usage(); } } else if( match_prefix( argv[i], "-debug" ) ) { // dprintf to console Termlog = 1; p_funcs = get_param_functions(); dprintf_config( "DAEMON-TOOL", p_funcs ); set_debug_flags(NULL, D_FULLDEBUG|D_SECURITY); } else if( match_prefix( argv[i], "-" ) ) { usage(); } else { usage(); } } // If we didn't get told what subsystem we should use, set it // to "TOOL". if( !get_mySubSystem()->isNameValid() ) { get_mySubSystem()->setName( "DAEMON-TOOL" ); } config( 0, true ); IpVerify ipverify; MyString line; while( line.readLine(input_fp) ) { line.chomp(); if( line.IsEmpty() || line[0] == '#' ) { printf("%s\n",line.Value()); continue; } StringList fields(line.Value()," "); fields.rewind(); char const *perm_str = fields.next(); char const *fqu = fields.next(); char const *ip = fields.next(); char const *expected = fields.next(); MyString sin_str = generate_sinful(ip, 0); condor_sockaddr addr; if( !addr.from_sinful(sin_str) ) { fprintf(stderr,"Invalid ip address: %s\n",ip); exit(1); } DCpermission perm = StringToDCpermission(perm_str); if( perm == LAST_PERM ) { fprintf(stderr,"Invalid permission level: %s\n",perm_str); exit(1); } if( strcmp(fqu,"*") == 0 ) { fqu = ""; } char const *result; MyString reason; if( ipverify.Verify(perm,addr,fqu,&reason,&reason) != USER_AUTH_SUCCESS ) { result = "DENIED"; } else { result = "ALLOWED"; } if( expected && strcasecmp(expected,result) != 0 ) { printf("Got wrong result '%s' for '%s': reason: %s!\n", result,line.Value(),reason.Value()); printf("Aborting.\n"); exit(1); } if( expected ) { printf("%s\n",line.Value()); } else { printf("%s %s\n",line.Value(),result); } } }
// Note: for this to work correctly, it's vital that the events we generate // in recovery mode exactly match how they were output in "non-recovery" // mode, so we can compare timestamps, and, if the timestamp matches // the last pre-recovery timestamp, the entire event string. void JobstateLog::InitializeRecovery() { debug_printf( DEBUG_DEBUG_2, "JobstateLog::InitializeRecovery()\n" ); if ( !_jobstateLogFile ) { return; } // // Find the timestamp of the last "real" event written to the // jobstate.log file. Any events that we see in recovery mode // that have an earlier timestamp should *not* be re-written // to the jobstate.log file. Any events with later timestamps // should be written. Events with equal timestamps need to be // tested individually. // FILE *infile = safe_fopen_wrapper_follow( _jobstateLogFile, "r" ); if ( !infile ) { // This is a fatal error, because by the time we get here, // we should, at the very least, have written the // DAGMAN_STARTED "event". debug_printf( DEBUG_QUIET, "Could not open jobstate log file %s for reading.\n", _jobstateLogFile ); main_shutdown_graceful(); return; } MyString line; off_t startOfLastTimestamp = 0; while ( true ) { off_t currentOffset = ftell( infile ); if ( !line.readLine( infile ) ) { break; } time_t newTimestamp; MyString nodeName; int seqNum; if ( ParseLine( line, newTimestamp, nodeName, seqNum ) ) { // We don't want to look at "INTERNAL" events here, or we'll // get goofed up by our own DAGMAN_STARTED event, etc. if ( nodeName != INTERNAL_NAME ) { // Note: we don't absolutely rely on the timestamps // being in order -- the > below rather than == is // important in that case. if ( newTimestamp > _lastTimestampWritten ) { startOfLastTimestamp = currentOffset; _lastTimestampWritten = newTimestamp; } } } } debug_printf( DEBUG_DEBUG_2, "_lastTimestampWritten: %lu\n", (unsigned long)_lastTimestampWritten ); // // Now find all lines that match the last timestamp, and put // them into a hash table for future reference. // if ( fseek( infile, startOfLastTimestamp, SEEK_SET ) != 0 ) { debug_printf( DEBUG_QUIET, "Error seeking in jobstate log file %s.\n", _jobstateLogFile ); } while ( line.readLine( infile ) ) { time_t newTimestamp; MyString nodeName; int seqNum; if ( ParseLine( line, newTimestamp, nodeName, seqNum ) ) { if ( (newTimestamp == _lastTimestampWritten) && (nodeName != INTERNAL_NAME) ) { _lastTimestampLines.insert( line ); debug_printf( DEBUG_DEBUG_2, "Appended <%s> to _lastTimestampLines\n", line.Value() ); } } } fclose( infile ); }
int run_simple_docker_command(const std::string &command, const std::string &container, int timeout, CondorError &, bool ignore_output) { ArgList args; if ( ! add_docker_arg(args)) return -1; args.AppendArg( command ); args.AppendArg( container.c_str() ); MyString displayString; args.GetArgsStringForLogging( & displayString ); dprintf( D_FULLDEBUG, "Attempting to run: %s\n", displayString.c_str() ); #if 1 MyPopenTimer pgm; if (pgm.start_program( args, true, NULL, false ) < 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } if ( ! pgm.wait_and_close(timeout) || pgm.output_size() <= 0) { int error = pgm.error_code(); if( error ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to read results from '%s': '%s' (%d)\n", displayString.c_str(), pgm.error_str(), error ); if (pgm.was_timeout()) { dprintf( D_ALWAYS | D_FAILURE, "Declaring a hung docker\n"); return DockerAPI::docker_hung; } } else { dprintf( D_ALWAYS | D_FAILURE, "'%s' returned nothing.\n", displayString.c_str() ); } return -3; } // On a success, Docker writes the containerID back out. MyString line; line.readLine(pgm.output()); line.chomp(); line.trim(); if (!ignore_output && line != container.c_str()) { // Didn't get back the result I expected, report the error and check to see if docker is hung. dprintf( D_ALWAYS | D_FAILURE, "Docker %s failed, printing first few lines of output.\n", command.c_str()); for (int ii = 0; ii < 10; ++ii) { if ( ! line.readLine(pgm.output(), false)) break; dprintf( D_ALWAYS | D_FAILURE, "%s\n", line.c_str() ); } return -4; } #else // Read from Docker's combined output and error streams. FILE * dockerResults = my_popen( args, "r", 1 , 0, false); if( dockerResults == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } // On a success, Docker writes the containerID back out. char buffer[1024]; if( NULL == fgets( buffer, 1024, dockerResults ) ) { if( errno ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to read results from '%s': '%s' (%d)\n", displayString.c_str(), strerror( errno ), errno ); } else { dprintf( D_ALWAYS | D_FAILURE, "'%s' returned nothing.\n", displayString.c_str() ); } my_pclose( dockerResults ); return -3; } size_t length = strlen( buffer ); if (!ignore_output) { if( length < 1 || strncmp( buffer, container.c_str(), length - 1 ) != 0 ) { dprintf( D_ALWAYS | D_FAILURE, "Docker %s failed, printing first few lines of output.\n", command.c_str() ); dprintf( D_ALWAYS | D_FAILURE, "%s", buffer ); while( NULL != fgets( buffer, 1024, dockerResults ) ) { dprintf( D_ALWAYS | D_FAILURE, "%s", buffer ); } my_pclose( dockerResults ); return -4; } } my_pclose( dockerResults ); #endif return 0; }
int DockerAPI::inspect( const std::string & containerID, ClassAd * dockerAd, CondorError & /* err */ ) { if( dockerAd == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "dockerAd is NULL.\n" ); return -2; } ArgList inspectArgs; if ( ! add_docker_arg(inspectArgs)) return -1; inspectArgs.AppendArg( "inspect" ); inspectArgs.AppendArg( "--format" ); StringList formatElements( "ContainerId=\"{{.Id}}\" " "Pid={{.State.Pid}} " "Name=\"{{.Name}}\" " "Running={{.State.Running}} " "ExitCode={{.State.ExitCode}} " "StartedAt=\"{{.State.StartedAt}}\" " "FinishedAt=\"{{.State.FinishedAt}}\" " "DockerError=\"{{.State.Error}}\" " "OOMKilled=\"{{.State.OOMKilled}}\" " ); char * formatArg = formatElements.print_to_delimed_string( "\n" ); inspectArgs.AppendArg( formatArg ); free( formatArg ); inspectArgs.AppendArg( containerID ); MyString displayString; inspectArgs.GetArgsStringForLogging( & displayString ); dprintf( D_FULLDEBUG, "Attempting to run: %s\n", displayString.c_str() ); #if 1 MyPopenTimer pgm; if (pgm.start_program(inspectArgs, true, NULL, false) < 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -6; } MyStringSource * src = NULL; if (pgm.wait_and_close(default_timeout)) { src = &pgm.output(); } int expected_rows = formatElements.number(); dprintf( D_FULLDEBUG, "exit_status=%d, error=%d, %d bytes. expecting %d lines\n", pgm.exit_status(), pgm.error_code(), pgm.output_size(), expected_rows ); // If the output isn't exactly formatElements.number() lines long, // something has gone wrong and we'll at least be able to print out // the error message(s). std::vector<std::string> correctOutput(expected_rows); if (src) { MyString line; int i=0; while (line.readLine(*src,false)) { line.chomp(); //dprintf( D_FULLDEBUG, "\t[%2d] %s\n", i, line.c_str() ); if (i >= expected_rows) { if (line.empty()) continue; correctOutput.push_back(line.c_str()); } else { correctOutput[i] = line.c_str(); } std::string::iterator first = std::find(correctOutput[i].begin(), correctOutput[i].end(), '\"'); if (first != correctOutput[i].end()) { std::replace(++first, --correctOutput[i].end(), '\"','\''); } //dprintf( D_FULLDEBUG, "\tfix: %s\n", correctOutput[i].c_str() ); ++i; } } #else FILE * dockerResults = my_popen( inspectArgs, "r", 1 , 0, false); if( dockerResults == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "Unable to run '%s'.\n", displayString.c_str() ); return -6; } // If the output isn't exactly formatElements.number() lines long, // something has gone wrong and we'll at least be able to print out // the error message(s). char buffer[1024]; std::vector<std::string> correctOutput(formatElements.number()); for( int i = 0; i < formatElements.number(); ++i ) { if( fgets( buffer, 1024, dockerResults ) != NULL ) { correctOutput[i] = buffer; std::string::iterator first = std::find(correctOutput[i].begin(), correctOutput[i].end(), '\"'); if (first != correctOutput[i].end()) { std::replace(++first, -- --correctOutput[i].end(), '\"','\''); } } } my_pclose( dockerResults ); #endif int attrCount = 0; for( int i = 0; i < formatElements.number(); ++i ) { if( correctOutput[i].empty() || dockerAd->Insert( correctOutput[i].c_str() ) == FALSE ) { break; } ++attrCount; } if( attrCount != formatElements.number() ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to create classad from Docker output (%d). Printing up to the first %d (nonblank) lines.\n", attrCount, formatElements.number() ); for( int i = 0; i < formatElements.number() && ! correctOutput[i].empty(); ++i ) { dprintf( D_ALWAYS | D_FAILURE, "%s", correctOutput[i].c_str() ); } return -4; } dprintf( D_FULLDEBUG, "docker inspect printed:\n" ); for( int i = 0; i < formatElements.number() && ! correctOutput[i].empty(); ++i ) { dprintf( D_FULLDEBUG, "\t%s\n", correctOutput[i].c_str() ); } return 0; }
// // FIXME: We have a lot of boilerplate code in this function and file. // int DockerAPI::version( std::string & version, CondorError & /* err */ ) { ArgList versionArgs; if ( ! add_docker_arg(versionArgs)) return -1; versionArgs.AppendArg( "-v" ); MyString displayString; versionArgs.GetArgsStringForLogging( & displayString ); dprintf( D_FULLDEBUG, "Attempting to run: '%s'.\n", displayString.c_str() ); #if 1 MyPopenTimer pgm; if (pgm.start_program(versionArgs, true, NULL, false) < 0) { // treat 'file not found' as not really error int d_level = (pgm.error_code() == ENOENT) ? D_FULLDEBUG : (D_ALWAYS | D_FAILURE); dprintf(d_level, "Failed to run '%s' errno=%d %s.\n", displayString.c_str(), pgm.error_code(), pgm.error_str() ); return -2; } int exitCode; if ( ! pgm.wait_for_exit(default_timeout, &exitCode)) { pgm.close_program(1); dprintf( D_ALWAYS | D_FAILURE, "Failed to read results from '%s': '%s' (%d)\n", displayString.c_str(), pgm.error_str(), pgm.error_code() ); return -3; } if (pgm.output_size() <= 0) { dprintf( D_ALWAYS | D_FAILURE, "'%s' returned nothing.\n", displayString.c_str() ); return -3; } MyStringSource * src = &pgm.output(); MyString line; if (line.readLine(*src, false)) { line.chomp(); bool jansens = strstr( line.c_str(), "Jansens" ) != NULL; bool bad_size = ! src->isEof() || line.size() > 1024 || line.size() < (int)sizeof("Docker version "); if (bad_size && ! jansens) { // check second line of output for the word Jansens also. MyString tmp; tmp.readLine(*src, false); jansens = strstr( tmp.c_str(), "Jansens" ) != NULL; } if (jansens) { dprintf( D_ALWAYS | D_FAILURE, "The DOCKER configuration setting appears to point to OpenBox's docker. If you want to use Docker.IO, please set DOCKER appropriately in your configuration.\n" ); return -5; } else if (bad_size) { dprintf( D_ALWAYS | D_FAILURE, "Read more than one line (or a very long line) from '%s', which we think means it's not Docker. The (first line of the) trailing text was '%s'.\n", displayString.c_str(), line.c_str() ); return -5; } } if( exitCode != 0 ) { dprintf( D_ALWAYS, "'%s' did not exit successfully (code %d); the first line of output was '%s'.\n", displayString.c_str(), exitCode, line.c_str() ); return -4; } version = line.c_str(); #else FILE * dockerResults = my_popen( versionArgs, "r", 1 , 0, false); if( dockerResults == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } char buffer[1024]; if( NULL == fgets( buffer, 1024, dockerResults ) ) { if( errno ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to read results from '%s': '%s' (%d)\n", displayString.c_str(), strerror( errno ), errno ); } else { dprintf( D_ALWAYS | D_FAILURE, "'%s' returned nothing.\n", displayString.c_str() ); } my_pclose( dockerResults ); return -3; } if( NULL != fgets( buffer, 1024, dockerResults ) ) { if( strstr( buffer, "Jansens" ) != NULL ) { dprintf( D_ALWAYS | D_FAILURE, "The DOCKER configuration setting appears to point to OpenBox's docker. If you want to use Docker.IO, please set DOCKER appropriately in your configuration.\n" ); } else { dprintf( D_ALWAYS | D_FAILURE, "Read more than one line (or a very long line) from '%s', which we think means it's not Docker. The (first line of the) trailing text was '%s'.\n", displayString.c_str(), buffer ); } my_pclose( dockerResults ); return -5; } int exitCode = my_pclose( dockerResults ); if( exitCode != 0 ) { dprintf( D_ALWAYS, "'%s' did not exit successfully (code %d); the first line of output was '%s'.\n", displayString.c_str(), exitCode, buffer ); return -4; } size_t end = strlen(buffer); if (end > 0 && buffer[end-1] == '\n') { buffer[end-1] = '\0'; } version = buffer; #endif sscanf(version.c_str(), "Docker version %d.%d", &DockerAPI::majorVersion, &DockerAPI::minorVersion); return 0; }
int DockerAPI::detect( CondorError & err ) { // FIXME: Remove ::version() as a public API and return it from here, // because there's no point in doing this twice. std::string version; int rval = DockerAPI::version( version, err ); if( rval != 0 ) { dprintf(D_ALWAYS, "DockerAPI::detect() failed to detect the Docker version; assuming absent.\n" ); return -4; } ArgList infoArgs; if ( ! add_docker_arg(infoArgs)) return -1; infoArgs.AppendArg( "info" ); MyString displayString; infoArgs.GetArgsStringForLogging( & displayString ); dprintf( D_FULLDEBUG, "Attempting to run: '%s'.\n", displayString.c_str() ); #if 1 MyPopenTimer pgm; if (pgm.start_program(infoArgs, true, NULL, false) < 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } int exitCode; if ( ! pgm.wait_for_exit(default_timeout, &exitCode) || exitCode != 0) { pgm.close_program(1); MyString line; line.readLine(pgm.output(), false); line.chomp(); dprintf( D_ALWAYS, "'%s' did not exit successfully (code %d); the first line of output was '%s'.\n", displayString.c_str(), exitCode, line.c_str()); return -3; } if (IsFulldebug(D_ALWAYS)) { MyString line; do { line.readLine(pgm.output(), false); line.chomp(); dprintf( D_FULLDEBUG, "[docker info] %s\n", line.c_str() ); } while (line.readLine(pgm.output(), false)); } #else FILE * dockerResults = my_popen( infoArgs, "r", 1 , 0, false); if( dockerResults == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } // Even if we don't care about the success output, the failure output // can be handy for debugging... char buffer[1024]; std::vector< std::string > output; while( fgets( buffer, 1024, dockerResults ) != NULL ) { size_t end = strlen(buffer); if (end > 0 && buffer[end-1] == '\n') { buffer[end-1] = '\0'; } output.push_back( buffer ); } for( unsigned i = 0; i < output.size(); ++i ) { dprintf( D_FULLDEBUG, "[docker info] %s\n", output[i].c_str() ); } int exitCode = my_pclose( dockerResults ); if( exitCode != 0 ) { dprintf( D_ALWAYS, "'%s' did not exit successfully (code %d); the first line of output was '%s'.\n", displayString.c_str(), exitCode, output[0].c_str() ); return -3; } #endif return 0; }
int DockerAPI::rmi(const std::string &image, CondorError &err) { // First, try to remove the named image run_simple_docker_command("rmi", image, default_timeout, err, true); // That may have succeed or failed. It could have // failed if the image doesn't exist (anymore), or // if someone else deleted it outside of condor. // Check to see if the image still exists. If it // has been removed, return 0. ArgList args; if ( ! add_docker_arg(args)) return -1; args.AppendArg( "images" ); args.AppendArg( "-q" ); args.AppendArg( image ); MyString displayString; args.GetArgsStringForLogging( & displayString ); dprintf( D_FULLDEBUG, "Attempting to run: '%s'.\n", displayString.c_str() ); #if 1 MyPopenTimer pgm; if (pgm.start_program(args, true, NULL, false) < 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } int exitCode; if ( ! pgm.wait_for_exit(default_timeout, &exitCode) || exitCode != 0) { pgm.close_program(1); MyString line; line.readLine(pgm.output(), false); line.chomp(); dprintf( D_ALWAYS, "'%s' did not exit successfully (code %d); the first line of output was '%s'.\n", displayString.c_str(), exitCode, line.c_str()); return -3; } return pgm.output_size() > 0; #else FILE * dockerResults = my_popen( args, "r", 1 , 0, false); if( dockerResults == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } char buffer[1024]; std::vector< std::string > output; while( fgets( buffer, 1024, dockerResults ) != NULL ) { size_t end = strlen(buffer); if (end > 0 && buffer[end-1] == '\n') { buffer[end-1] = '\0'; } output.push_back( buffer ); } int exitCode = my_pclose( dockerResults ); if( exitCode != 0 ) { dprintf( D_ALWAYS, "'%s' did not exit successfully (code %d); the first line of output was '%s'.\n", displayString.c_str(), exitCode, output[0].c_str() ); return -3; } if (output.size() == 0) { return 0; } else { return 1; } #endif }
// Read the history from a single file and print it out. static void readHistoryFromFile(char *JobHistoryFileName) { int EndFlag = 0; int ErrorFlag = 0; int EmptyFlag = 0; ClassAd *ad = NULL; long offset = 0; bool BOF = false; // Beginning Of File MyString buf; FILE* LogFile=safe_fopen_wrapper(JobHistoryFileName,"r"); if (!LogFile) { fprintf(stderr,"History file (%s) not found or empty.\n", JobHistoryFileName); exit(1); } // In case of rotated history files, check if we have already reached the number of // matches specified by the user before reading the next file if (specifiedMatch != 0) { if (matchCount == specifiedMatch) { // Already found n number of matches, cleanup fclose(LogFile); return; } } if (backwards) { offset = findLastDelimiter(LogFile, JobHistoryFileName); } while(!EndFlag) { if (backwards) { // Read history file backwards if (BOF) { // If reached beginning of file break; } offset = findPrevDelimiter(LogFile, JobHistoryFileName, offset); if (offset == -1) { // Unable to match constraint break; } else if (offset != 0) { fseek(LogFile, offset, SEEK_SET); buf.readLine(LogFile); // Read one line to skip delimiter and adjust to actual offset of ad } else { // Offset set to 0 BOF = true; fseek(LogFile, offset, SEEK_SET); } } if( !( ad=new ClassAd(LogFile,"***", EndFlag, ErrorFlag, EmptyFlag) ) ){ fprintf( stderr, "Error: Out of memory\n" ); exit( 1 ); } if( ErrorFlag ) { printf( "\t*** Warning: Bad history file; skipping malformed ad(s)\n" ); ErrorFlag=0; if(ad) { delete ad; ad = NULL; } continue; } if( EmptyFlag ) { EmptyFlag=0; if(ad) { delete ad; ad = NULL; } continue; } // emit the ad here writeJobAd(ad); if(ad) { delete ad; ad = NULL; } } fclose(LogFile); return; }
// Read the history from a single file and print it out. static void readHistoryFromFileOld(const char *JobHistoryFileName, const char* constraint, ExprTree *constraintExpr) { int EndFlag = 0; int ErrorFlag = 0; int EmptyFlag = 0; ClassAd *ad = NULL; long offset = 0; bool BOF = false; // Beginning Of File MyString buf; int flags = 0; if( !backwards ) { // Currently, the file position manipulations used in -backwards // do not work with files > 2GB on platforms with 32-bit file // offsets. flags = O_LARGEFILE; } int LogFd = safe_open_wrapper_follow(JobHistoryFileName,flags,0); if (LogFd < 0) { fprintf(stderr,"Error opening history file %s: %s\n", JobHistoryFileName,strerror(errno)); #ifdef EFBIG if( (errno == EFBIG) && backwards ) { fprintf(stderr,"The -backwards option does not support files this large.\n"); } #endif exit(1); } FILE *LogFile = fdopen(LogFd,"r"); if (!LogFile) { fprintf(stderr,"Error opening history file %s: %s\n", JobHistoryFileName,strerror(errno)); exit(1); } // In case of rotated history files, check if we have already reached the number of // matches specified by the user before reading the next file if (specifiedMatch != 0) { if (matchCount == specifiedMatch) { // Already found n number of matches, cleanup fclose(LogFile); return; } } if (backwards) { offset = findLastDelimiter(LogFile, JobHistoryFileName); } if(longformat && use_xml) { std::string out; AddClassAdXMLFileHeader(out); printf("%s\n", out.c_str()); } while(!EndFlag) { if (backwards) { // Read history file backwards if (BOF) { // If reached beginning of file break; } offset = findPrevDelimiter(LogFile, JobHistoryFileName, offset); if (offset == -1) { // Unable to match constraint break; } else if (offset != 0) { fseek(LogFile, offset, SEEK_SET); buf.readLine(LogFile); // Read one line to skip delimiter and adjust to actual offset of ad } else { // Offset set to 0 BOF = true; fseek(LogFile, offset, SEEK_SET); } } if( !( ad=new ClassAd(LogFile,"***", EndFlag, ErrorFlag, EmptyFlag) ) ){ fprintf( stderr, "Error: Out of memory\n" ); exit( 1 ); } if( ErrorFlag ) { printf( "\t*** Warning: Bad history file; skipping malformed ad(s)\n" ); ErrorFlag=0; if(ad) { delete ad; ad = NULL; } continue; } if( EmptyFlag ) { EmptyFlag=0; if(ad) { delete ad; ad = NULL; } continue; } if (!constraint || constraint[0]=='\0' || EvalBool(ad, constraintExpr)) { if (longformat) { if( use_xml ) { fPrintAdAsXML(stdout, *ad); } else { fPrintAd(stdout, *ad); } printf("\n"); } else { if (customFormat) { mask.display(stdout, ad); } else { displayJobShort(ad); } } matchCount++; // if control reached here, match has occured if (specifiedMatch != 0) { // User specified a match number if (matchCount == specifiedMatch) { // Found n number of matches, cleanup if (ad) { delete ad; ad = NULL; } fclose(LogFile); return; } } } if(ad) { delete ad; ad = NULL; } } if(longformat && use_xml) { std::string out; AddClassAdXMLFileFooter(out); printf("%s\n", out.c_str()); } fclose(LogFile); return; }
// Read the history from a single file and print it out. static void readHistoryFromFile(char *JobHistoryFileName, char* constraint, ExprTree *constraintExpr) { int EndFlag = 0; int ErrorFlag = 0; int EmptyFlag = 0; AttrList *ad = NULL; long offset = 0; bool BOF = false; // Beginning Of File MyString buf; FILE* LogFile=safe_fopen_wrapper(JobHistoryFileName,"r"); if (!LogFile) { fprintf(stderr,"History file (%s) not found or empty.\n", JobHistoryFileName); exit(1); } // In case of rotated history files, check if we have already reached the number of // matches specified by the user before reading the next file if (specifiedMatch != 0) { if (matchCount == specifiedMatch) { // Already found n number of matches, cleanup fclose(LogFile); return; } } if (backwards) { offset = findLastDelimiter(LogFile, JobHistoryFileName); } while(!EndFlag) { if (backwards) { // Read history file backwards if (BOF) { // If reached beginning of file break; } offset = findPrevDelimiter(LogFile, JobHistoryFileName, offset); if (offset == -1) { // Unable to match constraint break; } else if (offset != 0) { fseek(LogFile, offset, SEEK_SET); buf.readLine(LogFile); // Read one line to skip delimiter and adjust to actual offset of ad } else { // Offset set to 0 BOF = true; fseek(LogFile, offset, SEEK_SET); } } if( !( ad=new AttrList(LogFile,"***", EndFlag, ErrorFlag, EmptyFlag) ) ){ fprintf( stderr, "Error: Out of memory\n" ); exit( 1 ); } if( ErrorFlag ) { printf( "\t*** Warning: Bad history file; skipping malformed ad(s)\n" ); ErrorFlag=0; if(ad) { delete ad; ad = NULL; } continue; } if( EmptyFlag ) { EmptyFlag=0; if(ad) { delete ad; ad = NULL; } continue; } if (!constraint || EvalBool(ad, constraintExpr)) { if (longformat) { ad->fPrint(stdout); printf("\n"); } else { if (customFormat) { mask.display(stdout, ad); } else { displayJobShort(ad); } } matchCount++; // if control reached here, match has occured if (specifiedMatch != 0) { // User specified a match number if (matchCount == specifiedMatch) { // Found n number of matches, cleanup if (ad) { delete ad; ad = NULL; } fclose(LogFile); return; } } } if(ad) { delete ad; ad = NULL; } } fclose(LogFile); return; }
int DockerAPI::rm( const std::string & containerID, CondorError & /* err */ ) { ArgList rmArgs; if ( ! add_docker_arg(rmArgs)) return -1; rmArgs.AppendArg( "rm" ); rmArgs.AppendArg( "-f" ); // if for some reason still running, kill first rmArgs.AppendArg( "-v" ); // also remove the volume rmArgs.AppendArg( containerID.c_str() ); MyString displayString; rmArgs.GetArgsStringForLogging( & displayString ); dprintf( D_FULLDEBUG, "Attempting to run: %s\n", displayString.c_str() ); // Read from Docker's combined output and error streams. #if 1 MyPopenTimer pgm; if (pgm.start_program( rmArgs, true, NULL, false ) < 0) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } const char * got_output = pgm.wait_and_close(default_timeout); // On a success, Docker writes the containerID back out. MyString line; if ( ! got_output || ! line.readLine(pgm.output(), false)) { int error = pgm.error_code(); if( error ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to read results from '%s': '%s' (%d)\n", displayString.c_str(), pgm.error_str(), error ); if (pgm.was_timeout()) { dprintf( D_ALWAYS | D_FAILURE, "Declaring a hung docker\n"); return docker_hung; } } else { dprintf( D_ALWAYS | D_FAILURE, "'%s' returned nothing.\n", displayString.c_str() ); } return -3; } line.chomp(); line.trim(); if (line != containerID.c_str()) { // Didn't get back the result I expected, report the error and check to see if docker is hung. return check_if_docker_offline(pgm, "Docker remove", -4); } #else FILE * dockerResults = my_popen( rmArgs, "r", 1 , 0, false); if( dockerResults == NULL ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to run '%s'.\n", displayString.c_str() ); return -2; } // On a success, Docker writes the containerID back out. char buffer[1024]; if( NULL == fgets( buffer, 1024, dockerResults ) ) { if( errno ) { dprintf( D_ALWAYS | D_FAILURE, "Failed to read results from '%s': '%s' (%d)\n", displayString.c_str(), strerror( errno ), errno ); } else { dprintf( D_ALWAYS | D_FAILURE, "'%s' returned nothing.\n", displayString.c_str() ); } my_pclose( dockerResults ); return -3; } int length = strlen( buffer ); if( length < 1 || strncmp( buffer, containerID.c_str(), length - 1 ) != 0 ) { dprintf( D_ALWAYS | D_FAILURE, "Docker remove failed, printing first few lines of output.\n" ); dprintf( D_ALWAYS | D_FAILURE, "%s", buffer ); while( NULL != fgets( buffer, 1024, dockerResults ) ) { dprintf( D_ALWAYS | D_FAILURE, "%s", buffer ); } my_pclose( dockerResults ); return -4; } my_pclose( dockerResults ); #endif return 0; }