19 package org.sleuthkit.autopsy.ingest;
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.LinkedHashMap;
26 import java.util.List;
28 import java.util.concurrent.CopyOnWriteArrayList;
29 import java.util.concurrent.LinkedBlockingQueue;
30 import java.util.concurrent.atomic.AtomicLong;
31 import java.util.logging.Level;
32 import javax.swing.JOptionPane;
33 import org.netbeans.api.progress.ProgressHandle;
34 import org.openide.util.Cancellable;
35 import org.openide.util.NbBundle;
36 import org.openide.windows.WindowManager;
47 import org.
sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType;
49 import org.
sleuthkit.datamodel.IngestModuleInfo.IngestModuleType;
70 private static final AtomicLong
nextJobId =
new AtomicLong(0L);
71 private final long id;
74 private final List<AbstractFile>
files =
new ArrayList<>();
146 private static final IngestTasksScheduler
taskScheduler = IngestTasksScheduler.getInstance();
212 this.files.addAll(files);
214 this.doUI = runInteractively;
215 this.createTime =
new Date().getTime();
228 Map<String, IngestModuleTemplate> dataSourceModuleTemplates =
new LinkedHashMap<>();
229 Map<String, IngestModuleTemplate> fileModuleTemplates =
new LinkedHashMap<>();
231 if (
template.isDataSourceIngestModuleTemplate()) {
232 dataSourceModuleTemplates.put(
template.getModuleFactory().getClass().getCanonicalName(),
template);
234 if (
template.isFileIngestModuleTemplate()) {
235 fileModuleTemplates.put(
template.getModuleFactory().getClass().getCanonicalName(),
template);
243 IngestPipelinesConfiguration pipelineConfigs = IngestPipelinesConfiguration.getInstance();
254 firstStageDataSourceModuleTemplates.add(
template);
257 fileIngestModuleTemplates.add(
template);
263 this.firstStageDataSourceIngestPipeline =
new DataSourceIngestPipeline(
this, firstStageDataSourceModuleTemplates);
264 this.secondStageDataSourceIngestPipeline =
new DataSourceIngestPipeline(
this, secondStageDataSourceModuleTemplates);
271 for (
int i = 0; i < numberOfFileIngestThreads; ++i) {
272 FileIngestPipeline pipeline =
new FileIngestPipeline(
this, fileIngestModuleTemplates);
273 this.fileIngestPipelinesQueue.put(pipeline);
274 this.fileIngestPipelines.add(pipeline);
276 }
catch (InterruptedException ex) {
282 Thread.currentThread().interrupt();
286 this.
addIngestModules(firstStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
287 this.
addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase);
288 this.
addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
290 logErrorMessage(Level.WARNING,
"Failed to add ingest modules listing to case database", ex);
294 private void addIngestModules(List<IngestModuleTemplate> templates, IngestModuleType type, SleuthkitCase skCase)
throws TskCoreException {
296 ingestModules.add(skCase.addIngestModule(module.getModuleName(),
FactoryClassNameNormalizer.
normalize(module.getModuleFactory().getClass().getCanonicalName()), type, module.getModuleFactory().getModuleVersionNumber()));
316 List<IngestModuleTemplate> templates =
new ArrayList<>();
317 for (String moduleClassName : pipelineConfig) {
318 if (ingestModuleTemplates.containsKey(moduleClassName)) {
319 templates.add(ingestModuleTemplates.remove(moduleClassName));
339 String getExecutionContext() {
348 Content getDataSource() {
358 boolean shouldProcessUnallocatedSpace() {
367 FilesSet getFileIngestFilter() {
376 boolean hasIngestPipeline() {
389 return (this.firstStageDataSourceIngestPipeline.isEmpty() ==
false);
399 return (this.secondStageDataSourceIngestPipeline.isEmpty() ==
false);
408 if (!this.fileIngestPipelines.isEmpty()) {
409 return !this.fileIngestPipelines.get(0).isEmpty();
419 List<IngestModuleError> start() {
421 if (errors.isEmpty()) {
424 }
catch (TskCoreException | NoCurrentCaseException ex) {
425 logErrorMessage(Level.WARNING,
"Failed to add ingest job info to case database", ex);
445 List<IngestModuleError> errors =
new ArrayList<>();
450 errors.addAll(this.firstStageDataSourceIngestPipeline.startUp());
451 errors.addAll(this.secondStageDataSourceIngestPipeline.startUp());
458 if (errors.isEmpty()) {
459 for (FileIngestPipeline pipeline : this.fileIngestPipelinesQueue) {
460 errors.addAll(pipeline.startUp());
461 if (!errors.isEmpty()) {
467 while (!this.fileIngestPipelinesQueue.isEmpty()) {
468 FileIngestPipeline startedPipeline = this.fileIngestPipelinesQueue.poll();
469 if (startedPipeline.isRunning()) {
470 List<IngestModuleError> shutDownErrors = startedPipeline.shutDown();
471 if (!shutDownErrors.isEmpty()) {
497 this.estimatedFilesToProcess = this.dataSource.accept(
new GetFilesCountVisitor());
525 logInfoMessage(
"Scheduling first stage data source and file level analysis tasks");
528 logInfoMessage(
"Scheduling first stage data source level analysis tasks");
531 logInfoMessage(
"Scheduling file level analysis tasks, no first stage data source level analysis configured");
558 logInfoMessage(
"Scheduling second stage data source level analysis tasks");
568 String displayName = NbBundle.getMessage(this.getClass(),
569 "IngestJob.progress.dataSourceIngest.initialDisplayName",
570 this.dataSource.getName());
571 this.dataSourceIngestProgress = ProgressHandle.createHandle(displayName,
new Cancellable() {
573 public boolean cancel() {
580 DataSourceIngestCancellationPanel panel =
new DataSourceIngestCancellationPanel();
581 String dialogTitle = NbBundle.getMessage(
DataSourceIngestJob.this.getClass(),
"IngestJob.cancellationDialog.title");
582 JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), panel, dialogTitle, JOptionPane.OK_OPTION, JOptionPane.PLAIN_MESSAGE);
583 if (panel.cancelAllDataSourceIngestModules()) {
591 this.dataSourceIngestProgress.start();
592 this.dataSourceIngestProgress.switchToIndeterminate();
603 String displayName = NbBundle.getMessage(this.getClass(),
604 "IngestJob.progress.fileIngest.displayName",
605 this.dataSource.getName());
606 this.fileIngestProgress = ProgressHandle.createHandle(displayName,
new Cancellable() {
608 public boolean cancel() {
617 this.fileIngestProgress.start();
618 this.fileIngestProgress.switchToDeterminate((
int) this.estimatedFilesToProcess);
630 switch (this.stage) {
652 List<IngestModuleError> errors =
new ArrayList<>();
653 while (!this.fileIngestPipelinesQueue.isEmpty()) {
654 FileIngestPipeline pipeline = fileIngestPipelinesQueue.poll();
655 if (pipeline.isRunning()) {
656 errors.addAll(pipeline.shutDown());
659 if (!errors.isEmpty()) {
667 if (this.dataSourceIngestProgress != null) {
668 this.dataSourceIngestProgress.finish();
669 this.dataSourceIngestProgress = null;
676 if (this.fileIngestProgress != null) {
677 this.fileIngestProgress.finish();
678 this.fileIngestProgress = null;
704 if (this.dataSourceIngestProgress != null) {
705 this.dataSourceIngestProgress.finish();
706 this.dataSourceIngestProgress = null;
710 if (ingestJob != null) {
711 if (this.cancelled) {
713 ingestJob.setIngestJobStatus(IngestJobStatusType.CANCELLED);
714 }
catch (TskCoreException ex) {
715 logErrorMessage(Level.WARNING,
"Failed to update ingest job status in case database", ex);
719 ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED);
720 }
catch (TskCoreException ex) {
721 logErrorMessage(Level.WARNING,
"Failed to update ingest job status in case database", ex);
725 this.ingestJob.setEndDateTime(
new Date());
726 }
catch (TskCoreException ex) {
727 logErrorMessage(Level.WARNING,
"Failed to set job end date in case database", ex);
730 this.parentJob.dataSourceJobFinished(
this);
739 void process(DataSourceIngestTask task) {
742 if (!this.isCancelled() && !this.currentDataSourceIngestPipeline.isEmpty()) {
743 List<IngestModuleError> errors =
new ArrayList<>();
744 errors.addAll(this.currentDataSourceIngestPipeline.process(task));
745 if (!errors.isEmpty()) {
757 if (null != this.dataSourceIngestProgress) {
758 this.dataSourceIngestProgress.finish();
759 this.dataSourceIngestProgress = null;
765 DataSourceIngestJob.taskScheduler.notifyTaskCompleted(task);
781 void process(FileIngestTask task)
throws InterruptedException {
783 if (!this.isCancelled()) {
784 FileIngestPipeline pipeline = this.fileIngestPipelinesQueue.take();
785 if (!pipeline.isEmpty()) {
786 AbstractFile file = task.getFile();
794 if (this.processedFiles <= this.estimatedFilesToProcess) {
795 this.fileIngestProgress.progress(file.getName(), (int) this.processedFiles);
797 this.fileIngestProgress.progress(file.getName(), (int) this.estimatedFilesToProcess);
799 this.filesInProgress.add(file.getName());
806 List<IngestModuleError> errors =
new ArrayList<>();
807 errors.addAll(pipeline.process(task));
808 if (!errors.isEmpty()) {
812 if (this.doUI && !this.cancelled) {
818 this.filesInProgress.remove(file.getName());
819 if (this.filesInProgress.size() > 0) {
820 this.fileIngestProgress.progress(this.filesInProgress.get(0));
822 this.fileIngestProgress.progress(
"");
827 this.fileIngestPipelinesQueue.put(pipeline);
830 DataSourceIngestJob.taskScheduler.notifyTaskCompleted(task);
842 void addFiles(List<AbstractFile> files) {
843 if (DataSourceIngestJob.Stages.FIRST ==
this.stage) {
844 DataSourceIngestJob.taskScheduler.fastTrackFileIngestTasks(
this, files);
846 logErrorMessage(Level.SEVERE,
"Adding files to job during second stage analysis not supported");
864 void updateDataSourceIngestProgressBarDisplayName(String displayName) {
865 if (this.doUI && !this.cancelled) {
867 this.dataSourceIngestProgress.setDisplayName(displayName);
880 void switchDataSourceIngestProgressBarToDeterminate(
int workUnits) {
881 if (this.doUI && !this.cancelled) {
883 if (null != this.dataSourceIngestProgress) {
884 this.dataSourceIngestProgress.switchToDeterminate(workUnits);
895 void switchDataSourceIngestProgressBarToIndeterminate() {
896 if (this.doUI && !this.cancelled) {
898 if (null != this.dataSourceIngestProgress) {
899 this.dataSourceIngestProgress.switchToIndeterminate();
911 void advanceDataSourceIngestProgressBar(
int workUnits) {
912 if (this.doUI && !this.cancelled) {
914 if (null != this.dataSourceIngestProgress) {
915 this.dataSourceIngestProgress.progress(
"", workUnits);
927 void advanceDataSourceIngestProgressBar(String currentTask) {
928 if (this.doUI && !this.cancelled) {
930 if (null != this.dataSourceIngestProgress) {
931 this.dataSourceIngestProgress.progress(currentTask);
945 void advanceDataSourceIngestProgressBar(String currentTask,
int workUnits) {
946 if (this.doUI && !this.cancelled) {
948 this.dataSourceIngestProgress.progress(currentTask, workUnits);
960 boolean currentDataSourceIngestModuleIsCancelled() {
970 void currentDataSourceIngestModuleCancellationCompleted(String moduleDisplayName) {
971 this.currentDataSourceIngestModuleCancelled =
false;
972 this.cancelledDataSourceIngestModules.add(moduleDisplayName);
983 this.dataSourceIngestProgress.finish();
984 this.dataSourceIngestProgress = null;
995 DataSourceIngestPipeline.PipelineModule getCurrentDataSourceIngestModule() {
996 if (null != this.currentDataSourceIngestPipeline) {
997 return this.currentDataSourceIngestPipeline.getCurrentlyRunningModule();
1007 void cancelCurrentDataSourceIngestModule() {
1008 this.currentDataSourceIngestModuleCancelled =
true;
1017 void cancel(IngestJob.CancellationReason reason) {
1018 this.cancelled =
true;
1020 DataSourceIngestJob.taskScheduler.cancelPendingTasksForIngestJob(
this);
1024 if (null != dataSourceIngestProgress) {
1025 dataSourceIngestProgress.setDisplayName(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.dataSourceIngest.initialDisplayName", dataSource.getName()));
1026 dataSourceIngestProgress.progress(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.cancelling"));
1031 if (null != this.fileIngestProgress) {
1032 this.fileIngestProgress.setDisplayName(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.fileIngest.displayName", this.dataSource.getName()));
1033 this.fileIngestProgress.progress(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.cancelling"));
1046 void setCurrentFileIngestModule(String moduleName, String taskName) {
1047 this.currentFileIngestModule = moduleName;
1048 this.currentFileIngestTask = taskName;
1057 boolean isCancelled() {
1066 IngestJob.CancellationReason getCancellationReason() {
1077 logger.log(Level.INFO, String.format(
"%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(),
id));
1089 logger.log(level, String.format(
"%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(),
id), throwable);
1100 logger.log(level, String.format(
"%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(),
id));
1110 logErrorMessage(Level.SEVERE, String.format(
"%s experienced an error during analysis", error.getModuleDisplayName()), error.getThrowable());
1119 Snapshot getSnapshot(
boolean getIngestTasksSnapshot) {
1125 boolean fileIngestRunning =
false;
1126 Date fileIngestStartTime = null;
1128 for (FileIngestPipeline pipeline : this.fileIngestPipelines) {
1129 if (pipeline.isRunning()) {
1130 fileIngestRunning =
true;
1132 Date pipelineStartTime = pipeline.getStartTime();
1133 if (null != pipelineStartTime && (null == fileIngestStartTime || pipelineStartTime.before(fileIngestStartTime))) {
1134 fileIngestStartTime = pipelineStartTime;
1138 long processedFilesCount = 0;
1139 long estimatedFilesToProcessCount = 0;
1140 long snapShotTime =
new Date().getTime();
1141 IngestJobTasksSnapshot tasksSnapshot = null;
1143 if (getIngestTasksSnapshot) {
1147 snapShotTime =
new Date().getTime();
1149 tasksSnapshot = DataSourceIngestJob.taskScheduler.getTasksSnapshotForJob(
id);
1153 return new Snapshot(this.dataSource.getName(),
id,
createTime,
1154 getCurrentDataSourceIngestModule(), fileIngestRunning, fileIngestStartTime,
1156 processedFilesCount, estimatedFilesToProcessCount, snapShotTime, tasksSnapshot);
1162 public static final class Snapshot implements Serializable {
1184 Snapshot(String dataSourceName,
long jobId,
long jobStartTime, PipelineModule dataSourceIngestModule,
1185 boolean fileIngestRunning, Date fileIngestStartTime,
1187 long processedFiles,
long estimatedFilesToProcess,
1188 long snapshotTime, IngestJobTasksSnapshot tasksSnapshot) {
1189 this.dataSource = dataSourceName;
1192 this.dataSourceLevelIngestModule = dataSourceIngestModule;
1198 this.cancelledDataSourceModules = cancelledModules;
1202 this.snapShotTime = snapshotTime;
1212 long getSnapshotTime() {
1222 String getDataSource() {
1242 long getJobStartTime() {
1246 DataSourceIngestPipeline.PipelineModule getDataSourceLevelIngestModule() {
1250 boolean getFileIngestIsRunning() {
1254 Date getFileIngestStartTime() {
1265 return (
double) processedFiles / ((snapShotTime -
jobStartTime) / 1000);
1273 long getFilesProcessed() {
1283 long getFilesEstimated() {
1287 long getRootQueueSize() {
1288 if (null == this.tasksSnapshot) {
1291 return this.tasksSnapshot.getRootQueueSize();
1294 long getDirQueueSize() {
1295 if (null == this.tasksSnapshot) {
1298 return this.tasksSnapshot.getDirectoryTasksQueueSize();
1301 long getFileQueueSize() {
1302 if (null == this.tasksSnapshot) {
1305 return this.tasksSnapshot.getFileQueueSize();
1308 long getDsQueueSize() {
1309 if (null == this.tasksSnapshot) {
1312 return this.tasksSnapshot.getDsQueueSize();
1315 long getRunningListSize() {
1316 if (null == this.tasksSnapshot) {
1319 return this.tasksSnapshot.getRunningListSize();
1322 boolean isCancelled() {
1331 IngestJob.CancellationReason getCancellationReason() {
1342 List<String> getCancelledDataSourceIngestModules() {
1343 return Collections.unmodifiableList(this.cancelledDataSourceModules);
transient final List< String > cancelledDataSourceModules
void logErrorMessage(Level level, String message)
final List< AbstractFile > files
final boolean fileIngestRunning
transient final boolean jobCancelled
static synchronized IngestManager getInstance()
final long processedFiles
void logErrorMessage(Level level, String message, Throwable throwable)
transient final CancellationReason jobCancellationReason
void logIngestModuleErrors(List< IngestModuleError > errors)
final List< String > cancelledDataSourceIngestModules
DataSourceIngestPipeline firstStageDataSourceIngestPipeline
List< IngestModuleTemplate > getEnabledIngestModuleTemplates()
final IngestJobSettings settings
void checkForStageCompleted()
static final long serialVersionUID
final List< IngestModuleInfo > ingestModules
String getExecutionContext()
final Object stageCompletionCheckLock
volatile IngestJobInfo ingestJob
final IngestJobTasksSnapshot tasksSnapshot
void logInfoMessage(String message)
final Object dataSourceIngestPipelineLock
static List< IngestModuleTemplate > getConfiguredIngestModuleTemplates(Map< String, IngestModuleTemplate > ingestModuleTemplates, List< String > pipelineConfig)
DataSourceIngestPipeline currentDataSourceIngestPipeline
transient final PipelineModule dataSourceLevelIngestModule
final long estimatedFilesToProcess
ProgressHandle fileIngestProgress
final Object dataSourceIngestProgressLock
String currentFileIngestTask
boolean hasFileIngestPipeline()
boolean getProcessUnallocatedSpace()
boolean hasSecondStageDataSourceIngestPipeline()
volatile boolean currentDataSourceIngestModuleCancelled
SleuthkitCase getSleuthkitCase()
long estimatedFilesToProcess
void startDataSourceIngestProgressBar()
DataSourceIngestPipeline secondStageDataSourceIngestPipeline
boolean hasFirstStageDataSourceIngestPipeline()
int getNumberOfFileIngestThreads()
final Object fileIngestProgressLock
void addIngestModules(List< IngestModuleTemplate > templates, IngestModuleType type, SleuthkitCase skCase)
List< IngestModuleError > startUpIngestPipelines()
static String getLocalHostName()
synchronized static Logger getLogger(String name)
static final Logger logger
static Case getCurrentCaseThrows()
final List< String > filesInProgress
void startFileIngestProgressBar()
void createIngestPipelines()
final LinkedBlockingQueue< FileIngestPipeline > fileIngestPipelinesQueue
final List< FileIngestPipeline > fileIngestPipelines
String currentFileIngestModule
final IngestJob parentJob
ProgressHandle dataSourceIngestProgress
final Date fileIngestStartTime
volatile IngestJob.CancellationReason cancellationReason
volatile boolean cancelled
static final IngestTasksScheduler taskScheduler
static final AtomicLong nextJobId
static String normalize(String canonicalClassName)