int Job::declare_file(const MyString &name, filesize_t size, CondorError &errstack) { JobFile *ignored; JobFile jobFile; jobFile.size = size; jobFile.currentOffset = 0; jobFile.name = name; jobFile.file = safe_open_wrapper_follow((spoolDirectory + DIR_DELIM_STRING + jobFile.name).Value(), O_WRONLY | O_CREAT | _O_BINARY, 0600); if (-1 != jobFile.file) { if (0 == declaredFiles.lookup(name, ignored)) { close(jobFile.file); errstack.pushf("SOAP", ALREADYEXISTS, "File '%s' already declared.", name.Value()); return 4; } if (declaredFiles.insert(name, jobFile)) { close(jobFile.file); errstack.pushf("SOAP", FAIL, "Failed to record file '%s'.", name.Value()); return 2; } } else { // If there is a path delimiter in the name we assume that // the client knows what she is doing and will set a // proper Iwd later on. If there is no path delimiter we // have a problem. if (-1 != name.FindChar(DIR_DELIM_CHAR)) { dprintf(D_FULLDEBUG, "Failed to open '%s' for writing, reason: %s\n", (spoolDirectory+DIR_DELIM_STRING+jobFile.name).Value(), strerror(errno)); errstack.pushf("SOAP", FAIL, "Failed to open '%s' for writing, reason: %s", name.Value(), strerror(errno)); return 3; } } return 0; }
int Job::get_file(const MyString &name, int offset, int length, unsigned char *&data, CondorError &errstack) { memset(data, 0, length); int file = safe_open_wrapper_follow((spoolDirectory + DIR_DELIM_STRING + name).Value(), O_RDONLY | _O_BINARY, 0); if (-1 != file) { if (-1 == lseek(file, offset, SEEK_SET)) { close(file); errstack.pushf("SOAP", FAIL, "Failed to lseek in file '%s', reason: %s", name.Value(), strerror(errno)); return 2; } int result; if (-1 == (result = full_read(file, data, sizeof(unsigned char) * length))) { close(file); errstack.pushf("SOAP", FAIL, "Failed to read from file '%s', wanted to " "read %d bytes but received %d", name.Value(), length, result); return 3; } if (-1 == close(file)) { errstack.pushf("SOAP", FAIL, "Failed to close file '%s', reason: %s", name.Value(), strerror(errno)); return 4; } } else { errstack.pushf("SOAP", FAIL, "Failed to open file '%s', reason: %s", name.Value(), strerror(errno)); return 1; } return 0; }
int Job::put_file(const MyString &name, int offset, char * data, int data_length, CondorError &errstack) { JobFile jobFile; if (-1 == declaredFiles.lookup(name, jobFile)) { errstack.pushf("SOAP", FAIL, "File '%s' has not been declared.", name.Value()); return 1; } if (-1 != jobFile.file) { if (-1 == lseek(jobFile.file, offset, SEEK_SET)) { errstack.pushf("SOAP", FAIL, "Failed to lseek in file '%s', reason: %s", name.Value(), strerror(errno)); return 2; } int result; if (data_length != (result = full_write(jobFile.file, data, data_length))) { errstack.pushf("SOAP", FAIL, "Failed to write to from file '%s', wanted to write %d bytes but was only able to write %d", name.Value(), data_length, result); return 3; } } else { errstack.pushf("SOAP", FAIL, "Failed to open file '%s', it should not " "contain any path separators.", name.Value()); return 5; } return 0; }
/////////////////////////////////////////////////////////////////////////////// // Note: on Unix/Linux, the file ID is a string encoding the combination of // device number and inode; on Windows the file ID is simply the value // _fullpath() returns on the path we're given. The Unix/Linux version // is preferable because it will work correctly even if there are hard // links to log files; but there are no inodes on Windows, so we're // doing what we can. bool GetFileID( const MyString &filename, MyString &fileID, CondorError &errstack ) { // Make sure the log file exists. Even though we may later call // InitializeFile(), we have to make sure the file exists here // first so we make sure that the file exists and we can therefore // get an inode or real path for it. // We *don't* want to truncate the file here, though, because // we don't know for sure whether it's the first time we're seeing // it. if ( access( filename.Value(), F_OK ) != 0 ) { if ( !MultiLogFiles::InitializeFile( filename.Value(), false, errstack ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error initializing log file %s", filename.Value() ); return false; } } #ifdef WIN32 char *tmpRealPath = realpath( filename.Value(), NULL ); if ( !tmpRealPath ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error (%d, %s) getting real path for specified path %s", errno, strerror( errno ), filename.Value() ); return false; } fileID = tmpRealPath; free( tmpRealPath ); #else StatWrapper swrap; if ( swrap.Stat( filename.Value() ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting inode for log file %s", filename.Value() ); return false; } fileID.formatstr( "%llu:%llu", (unsigned long long)swrap.GetBuf()->st_dev, (unsigned long long)swrap.GetBuf()->st_ino ); #endif return true; }
bool ODSHistoryFile::init(CondorError &errstack) { StatWrapper stat_wrapper; if (stat_wrapper.Stat(m_name.c_str())) { errstack.pushf("ODSHistoryFile::init", 1, "Failed to stat %s: %d (%s)\n", m_name.c_str(), stat_wrapper.GetErrno(), strerror(stat_wrapper.GetErrno())); return false; } m_stat = (StatStructType *) malloc(sizeof(StatStructType)); ASSERT(m_stat); memcpy(m_stat, stat_wrapper.GetBuf(), sizeof(StatStructType)); if (!S_ISREG(m_stat->st_mode)) { errstack.pushf("ODSHistoryFile::init", 2, "%s: not a regular file\n", m_name.c_str()); return false; } m_file = safe_fopen_wrapper(m_name.c_str(), "r"); if (NULL == m_file) { errstack.pushf("ODSHistoryFile::init", 4, "Failed to fopen %s: %d (%s)\n", m_name.c_str(), errno, strerror(errno)); return false; } m_writer = new ODSMongodbOps(DB_NAME); if (!m_writer->init("localhost")) { errstack.pushf("ODSHistoryFile::init", 5, "Unable to init ODS writer\n"); return false; } return true; }
int Job::get_spool_list(List<FileInfo> &file_list, CondorError &errstack) { StatInfo directoryInfo(spoolDirectory.Value()); if (directoryInfo.IsDirectory()) { Directory directory(spoolDirectory.Value()); const char * name; FileInfo *info; while (NULL != (name = directory.Next())) { info = new FileInfo(); info->initialize(name, directory.GetFileSize()); ASSERT(info); if (!file_list.Append(info)) { errstack.pushf("SOAP", FAIL, "Error adding %s to file list.", name); return 2; } } return 0; } else { dprintf(D_ALWAYS, "spoolDirectory == '%s'\n", spoolDirectory.Value()); errstack.pushf("SOAP", FAIL, "spool directory '%s' is not actually a directory.", spoolDirectory.Value()); return 1; } }
bool MultiLogFiles::InitializeFile(const char *filename, bool truncate, CondorError &errstack) { dprintf( D_LOG_FILES, "MultiLogFiles::InitializeFile(%s, %d)\n", filename, (int)truncate ); int flags = O_WRONLY; if ( truncate ) { flags |= O_TRUNC; dprintf( D_ALWAYS, "MultiLogFiles: truncating log file %s\n", filename ); } // Two-phase attempt at open here is to make things work if // a log file is a symlink to another file (see gittrac #2704). int fd = safe_create_fail_if_exists( filename, flags ); if ( fd < 0 && errno == EEXIST ) { fd = safe_open_no_create_follow( filename, flags ); } if ( fd < 0 ) { errstack.pushf("MultiLogFiles", UTIL_ERR_OPEN_FILE, "Error (%d, %s) opening file %s for creation " "or truncation", errno, strerror( errno ), filename ); return false; } if ( close( fd ) != 0 ) { errstack.pushf("MultiLogFiles", UTIL_ERR_CLOSE_FILE, "Error (%d, %s) closing file %s for creation " "or truncation", errno, strerror( errno ), filename ); return false; } return true; }
bool MultiLogFiles::makePathAbsolute(MyString &filename, CondorError &errstack) { if ( !fullpath(filename.Value()) ) { // I'd like to use realpath() here, but I'm not sure // if that's portable across all platforms. wenger 2009-01-09. MyString currentDir; if ( !condor_getcwd(currentDir) ) { errstack.pushf( "MultiLogFiles", UTIL_ERR_GET_CWD, "ERROR: condor_getcwd() failed with errno %d (%s) at %s:%d", errno, strerror(errno), __FILE__, __LINE__); return false; } filename = currentDir + DIR_DELIM_STRING + filename; } return true; }
//--------------------------------------------------------------------------- bool Job::UnmonitorLogFile( ReadMultipleUserLogs &condorLogReader, ReadMultipleUserLogs &storkLogReader ) { debug_printf( DEBUG_DEBUG_2, "Unmonitoring log file <%s> for node %s\n", GetLogFile(), GetJobName() ); if ( !_logIsMonitored ) { debug_printf( DEBUG_DEBUG_1, "Warning: log file for node " "%s is already unmonitored\n", GetJobName() ); return true; } ReadMultipleUserLogs &logReader = (_jobType == TYPE_CONDOR) ? condorLogReader : storkLogReader; debug_printf( DEBUG_DEBUG_1, "Unmonitoring log file <%s> for node %s\n", GetLogFile(), GetJobName() ); CondorError errstack; bool result = logReader.unmonitorLogFile( GetLogFile(), errstack ); if ( !result ) { errstack.pushf( "DAGMan::Job", DAGMAN_ERR_LOG_FILE, "ERROR: Unable to unmonitor log " "file for node %s", GetJobName() ); debug_printf( DEBUG_QUIET, "%s\n", errstack.getFullText().c_str() ); EXCEPT( "Fatal log file monitoring error!\n" ); } if ( result ) { delete [] _logFile; _logFile = NULL; _logIsMonitored = false; } return result; }
int Job::initialize(CondorError &errstack) { char * Spool = param("SPOOL"); ASSERT(Spool); char *ckpt_name = gen_ckpt_name(Spool, id.cluster, id.proc, 0); spoolDirectory = ckpt_name; free(ckpt_name); ckpt_name = NULL; if (Spool) { free(Spool); Spool = NULL; } struct stat stats; if (-1 == stat(spoolDirectory.Value(), &stats)) { if (ENOENT == errno && spoolDirectory.Length() != 0) { // We assume here that the job is not a standard universe // job. Spooling works differently for standard universe. // Unfortunately, we might not know the job universe // yet, so standard universe is problematic with SOAP // (and always has been). if( !SpooledJobFiles::createJobSpoolDirectory_PRIV_CONDOR(id.cluster,id.proc,false) ) { errstack.pushf("SOAP", FAIL, "Creation of spool directory '%s' failed, " "reason: %s", spoolDirectory.Value(), strerror(errno)); return 1; } else { dprintf(D_FULLDEBUG, "mkdir(%s) succeeded.\n", spoolDirectory.Value()); } } else { dprintf(D_FULLDEBUG, "ERROR: stat(%s) errno: %d (%s)\n", spoolDirectory.Value(), errno, strerror(errno)); errstack.pushf("SOAP", FAIL, "stat(%s) failed, reason: %s", spoolDirectory.Value(), strerror(errno)); return 2; } } else { dprintf(D_FULLDEBUG, "WARNING: Job '%d.%d''s spool '%s' already exists.\n", id.cluster, id.proc, spoolDirectory.Value()); } return 0; }
int Job::get_file(const MyString &name, int offset, int length, unsigned char *&data, CondorError &errstack) { #if !defined(WIN32) TemporaryPrivSentry sentry( true ); if ( param_boolean( "CHOWN_JOB_SPOOL_FILES", false ) == false ) { ClassAd *job_ad = GetJobAd_as_ClassAd( id.cluster, id.proc ); if ( job_ad == NULL ) { errstack.pushf("SOAP", FAIL, "Failed to retrieve job ad for file '%s'", name.Value()); return 5; } if ( !init_user_ids_from_ad( *job_ad ) ) { errstack.pushf("SOAP", FAIL, "Failed to init user ids for file '%s'", name.Value()); return 6; } set_user_priv(); } #endif int file = safe_open_wrapper_follow((spoolDirectory + DIR_DELIM_STRING + name).Value(), O_RDONLY | _O_BINARY, 0); if (-1 != file) { if (-1 == lseek(file, offset, SEEK_SET)) { close(file); errstack.pushf("SOAP", FAIL, "Failed to lseek in file '%s', reason: %s", name.Value(), strerror(errno)); return 2; } int result; if (-1 == (result = full_read(file, data, sizeof(unsigned char) * length))) { close(file); errstack.pushf("SOAP", FAIL, "Failed to read from file '%s', wanted to " "read %d bytes but received %d", name.Value(), length, result); return 3; } if (-1 == close(file)) { errstack.pushf("SOAP", FAIL, "Failed to close file '%s', reason: %s", name.Value(), strerror(errno)); return 4; } } else { errstack.pushf("SOAP", FAIL, "Failed to open file '%s', reason: %s", name.Value(), strerror(errno)); return 1; } return 0; }
int Job::submit(const struct condor__ClassAdStruct &jobAd, CondorError &errstack) { int i, rval; // XXX: This is ugly, and only should happen when spooling, // i.e. not always with cedar. rval = SetAttributeString(id.cluster, id.proc, ATTR_JOB_IWD, spoolDirectory.Value()); if (rval < 0) { errstack.pushf("SOAP", FAIL, "Failed to set job %d.%d's %s attribute to '%s'.", id.cluster, id.proc, ATTR_JOB_IWD, spoolDirectory.Value()); return rval; } StringList transferFiles; MyString currentKey; JobFile jobFile; declaredFiles.startIterations(); while (declaredFiles.iterate(currentKey, jobFile)) { transferFiles.append(jobFile.name.Value()); } char *fileList = NULL; if (0 == transferFiles.number()) { fileList = strdup(""); } else { fileList = transferFiles.print_to_string(); ASSERT(fileList); } rval = SetAttributeString(id.cluster, id.proc, ATTR_TRANSFER_INPUT_FILES, fileList); if (fileList) { free(fileList); fileList = NULL; } if (rval < 0) { errstack.pushf("SOAP", FAIL, "Failed to set job %d.%d's %s attribute.", id.cluster, id.proc, ATTR_TRANSFER_INPUT_FILES); return rval; } int found_iwd = 0; for (i = 0; i < jobAd.__size; i++) { const char* name = jobAd.__ptr[i].name; const char* value = jobAd.__ptr[i].value; if (!name) continue; if (!value) value="UNDEFINED"; // XXX: This is a quick fix. If processing MyType or // TargetType they should be ignored. Ideally we could // convert the ClassAdStruct to a ClassAd and then iterate // the ClassAd. if (0 == strcmp(name, ATTR_MY_TYPE) || 0 == strcmp(name, ATTR_TARGET_TYPE)) { continue; } if ( jobAd.__ptr[i].type == STRING_ATTR ) { // string type - put value in quotes as hint for ClassAd parser found_iwd = found_iwd || !strcmp(name, ATTR_JOB_IWD); rval = SetAttributeString(id.cluster, id.proc, name, value); } else { // all other types can be deduced by the ClassAd parser rval = SetAttribute(id.cluster, id.proc, name, value); } if ( rval < 0 ) { errstack.pushf("SOAP", FAIL, "Failed to set job %d.%d's %s attribute.", id.cluster, id.proc, name); return rval; } } // Trust the client knows what it is doing if there is an Iwd. if (!found_iwd) { // We need to make sure the Iwd is rewritten so files // in the spool directory can be found. rval = SetAttributeString(id.cluster, id.proc, ATTR_JOB_IWD, spoolDirectory.Value()); if (rval < 0) { errstack.pushf("SOAP", FAIL, "Failed to set %d.%d's %s attribute to '%s'.", id.cluster, id.proc, ATTR_JOB_IWD, spoolDirectory.Value()); return rval; } } return 0; }
//--------------------------------------------------------------------------- bool Job::MonitorLogFile( ReadMultipleUserLogs &condorLogReader, ReadMultipleUserLogs &storkLogReader, bool nfsIsError, bool recovery, const char *defaultNodeLog, bool usingDefault ) { debug_printf( DEBUG_DEBUG_2, "Attempting to monitor log file for node %s\n", GetJobName() ); if ( _logIsMonitored ) { debug_printf( DEBUG_DEBUG_1, "Warning: log file for node " "%s is already monitored\n", GetJobName() ); return true; } ReadMultipleUserLogs &logReader = (_jobType == TYPE_CONDOR) ? condorLogReader : storkLogReader; std::string logFileStr; if ( _jobType == TYPE_CONDOR ) { // We check to see if the user has specified a log file // If not, we give him a default MyString templogFileStr = MultiLogFiles::loadLogFileNameFromSubFile( _cmdFile, _directory, _logFileIsXml, usingDefault); logFileStr = templogFileStr.Value(); } else { StringList logFiles; MyString tmpResult = MultiLogFiles::loadLogFileNamesFromStorkSubFile( _cmdFile, _directory, logFiles ); if ( tmpResult != "" ) { debug_printf( DEBUG_QUIET, "Error getting Stork log file: %s\n", tmpResult.Value() ); LogMonitorFailed(); return false; } else if ( logFiles.number() != 1 ) { debug_printf( DEBUG_QUIET, "Error: %d Stork log files found " "in submit file %s; we want 1\n", logFiles.number(), _cmdFile ); LogMonitorFailed(); return false; } else { logFiles.rewind(); logFileStr = logFiles.next(); } } // Warn the user if the node's log file is in /tmp. if ( logFileStr.find( "/tmp" ) == 0 ) { debug_printf( DEBUG_QUIET, "Warning: " "Log file %s for node %s is in /tmp\n", logFileStr.c_str(), GetJobName() ); check_warning_strictness( usingDefault ? DAG_STRICT_2 : DAG_STRICT_1 ); } if ( logFileStr == "" ) { logFileStr = defaultNodeLog; _useDefaultLog = true; // Default User log is never XML // This could be specified in the submit file and should be // ignored. _logFileIsXml = false; debug_printf( DEBUG_NORMAL, "Unable to get log file from " "submit file %s (node %s); using default (%s)\n", _cmdFile, GetJobName(), logFileStr.c_str() ); append_default_log = false; } else { append_default_log = usingDefault; if( append_default_log ) { // DAGman is not going to look at the user-specified log. // It will look at the defaultNode log. logFileStr = defaultNodeLog; _useDefaultLog = false; _logFileIsXml = false; } } // This function returns true if the log file is on NFS and // that is an error. If the log file is on NFS, but nfsIsError // is false, it prints a warning but returns false. if ( MultiLogFiles::logFileNFSError( logFileStr.c_str(), nfsIsError ) ) { debug_printf( DEBUG_QUIET, "Error: log file %s on NFS\n", logFileStr.c_str() ); LogMonitorFailed(); return false; } delete [] _logFile; // Saving log file here in case submit file gets changed. _logFile = strnewp( logFileStr.c_str() ); debug_printf( DEBUG_DEBUG_2, "Monitoring log file <%s> for node %s\n", GetLogFile(), GetJobName() ); CondorError errstack; if ( !logReader.monitorLogFile( GetLogFile(), !recovery, errstack ) ) { errstack.pushf( "DAGMan::Job", DAGMAN_ERR_LOG_FILE, "ERROR: Unable to monitor log file for node %s", GetJobName() ); debug_printf( DEBUG_QUIET, "%s\n", errstack.getFullText().c_str() ); LogMonitorFailed(); EXCEPT( "Fatal log file monitoring error!\n" ); return false; } _logIsMonitored = true; return true; }
// Note: logfile is not passed as a reference because we need a local // copy to modify anyhow. bool ReadMultipleUserLogs::monitorLogFile( MyString logfile, bool truncateIfFirst, CondorError &errstack ) { dprintf( D_LOG_FILES, "ReadMultipleUserLogs::monitorLogFile(%s, %d)\n", logfile.Value(), truncateIfFirst ); MyString fileID; if ( !GetFileID( logfile, fileID, errstack ) ) { errstack.push( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting file ID in monitorLogFile()" ); return false; } LogFileMonitor *monitor; if ( allLogFiles.lookup( fileID, monitor ) == 0 ) { dprintf( D_LOG_FILES, "ReadMultipleUserLogs: found " "LogFileMonitor object for %s (%s)\n", logfile.Value(), fileID.Value() ); } else { dprintf( D_LOG_FILES, "ReadMultipleUserLogs: didn't " "find LogFileMonitor object for %s (%s)\n", logfile.Value(), fileID.Value() ); // Make sure the log file is in the correct state -- it must // exist, and be truncated if necessary. if ( !MultiLogFiles::InitializeFile( logfile.Value(), truncateIfFirst, errstack ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error initializing log file %s", logfile.Value() ); return false; } monitor = new LogFileMonitor( logfile ); ASSERT( monitor ); dprintf( D_LOG_FILES, "ReadMultipleUserLogs: created LogFileMonitor " "object for log file %s\n", logfile.Value() ); // Note: we're only putting a pointer to the LogFileMonitor // object into the hash table; the actual LogFileMonitor should // only be deleted in this object's destructor. if ( allLogFiles.insert( fileID, monitor ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error inserting %s into allLogFiles", logfile.Value() ); delete monitor; return false; } } if ( monitor->refCount < 1 ) { // Open the log file (return to previous location if it was // opened before). if ( monitor->state ) { // If we get here, we've monitored this log file before, // so restore the previous state. if ( monitor->stateError ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Monitoring log file %s fails because of " "previous error saving file state", logfile.Value() ); return false; } monitor->readUserLog = new ReadUserLog( *(monitor->state) ); } else { // Monitoring this log file for the first time, so create // the log reader from scratch. monitor->readUserLog = new ReadUserLog( monitor->logFile.Value() ); } if ( activeLogFiles.insert( fileID, monitor ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error inserting %s (%s) into activeLogFiles", logfile.Value(), fileID.Value() ); return false; } else { dprintf( D_LOG_FILES, "ReadMultipleUserLogs: added log " "file %s (%s) to active list\n", logfile.Value(), fileID.Value() ); } } monitor->refCount++; return true; }
// Note: logfile is not passed as a reference because we need a local // copy to modify anyhow. bool ReadMultipleUserLogs::unmonitorLogFile( MyString logfile, CondorError &errstack ) { dprintf( D_LOG_FILES, "ReadMultipleUserLogs::unmonitorLogFile(%s)\n", logfile.Value() ); MyString fileID; if ( !GetFileID( logfile, fileID, errstack ) ) { errstack.push( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting file ID in unmonitorLogFile()" ); return false; } LogFileMonitor *monitor; if ( activeLogFiles.lookup( fileID, monitor ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Didn't find LogFileMonitor object for log " "file %s (%s)!", logfile.Value(), fileID.Value() ); dprintf( D_ALWAYS, "ReadMultipleUserLogs error: %s\n", errstack.message() ); printAllLogMonitors( NULL ); return false; } dprintf( D_LOG_FILES, "ReadMultipleUserLogs: found " "LogFileMonitor object for %s (%s)\n", logfile.Value(), fileID.Value() ); monitor->refCount--; if ( monitor->refCount < 1 ) { // Okay, if we are no longer monitoring this file at all, // we need to close it. We do that by saving its state // into a ReadUserLog::FileState object (so we can go back // to the right place if we later monitor it again) and // then deleting the ReadUserLog object. dprintf( D_LOG_FILES, "Closing file <%s>\n", logfile.Value() ); if ( !monitor->state ) { monitor->state = new ReadUserLog::FileState(); if ( !ReadUserLog::InitFileState( *(monitor->state) ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Unable to initialize ReadUserLog::FileState " "object for log file %s", logfile.Value() ); monitor->stateError = true; delete monitor->state; monitor->state = NULL; return false; } } if ( !monitor->readUserLog->GetFileState( *(monitor->state) ) ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error getting state for log file %s", logfile.Value() ); monitor->stateError = true; delete monitor->state; monitor->state = NULL; return false; } delete monitor->readUserLog; monitor->readUserLog = NULL; // Now we remove this file from the "active" list, so // we don't check it the next time we get an event. if ( activeLogFiles.remove( fileID ) != 0 ) { errstack.pushf( "ReadMultipleUserLogs", UTIL_ERR_LOG_FILE, "Error removing %s (%s) from activeLogFiles", logfile.Value(), fileID.Value() ); dprintf( D_ALWAYS, "ReadMultipleUserLogs error: %s\n", errstack.message() ); printAllLogMonitors( NULL ); return false; } dprintf( D_LOG_FILES, "ReadMultipleUserLogs: removed " "log file %s (%s) from active list\n", logfile.Value(), fileID.Value() ); } return true; }