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;
69 private static final AtomicLong
nextJobId =
new AtomicLong(0L);
70 private final long id;
73 private final List<AbstractFile>
files =
new ArrayList<>();
145 private static final IngestTasksScheduler
taskScheduler = IngestTasksScheduler.getInstance();
211 this.files.addAll(files);
213 this.doUI = runInteractively;
214 this.createTime =
new Date().getTime();
227 Map<String, IngestModuleTemplate> dataSourceModuleTemplates =
new LinkedHashMap<>();
228 Map<String, IngestModuleTemplate> fileModuleTemplates =
new LinkedHashMap<>();
230 if (
template.isDataSourceIngestModuleTemplate()) {
231 dataSourceModuleTemplates.put(
template.getModuleFactory().getClass().getCanonicalName(),
template);
233 if (
template.isFileIngestModuleTemplate()) {
234 fileModuleTemplates.put(
template.getModuleFactory().getClass().getCanonicalName(),
template);
242 IngestPipelinesConfiguration pipelineConfigs = IngestPipelinesConfiguration.getInstance();
253 firstStageDataSourceModuleTemplates.add(
template);
256 fileIngestModuleTemplates.add(
template);
262 this.firstStageDataSourceIngestPipeline =
new DataSourceIngestPipeline(
this, firstStageDataSourceModuleTemplates);
263 this.secondStageDataSourceIngestPipeline =
new DataSourceIngestPipeline(
this, secondStageDataSourceModuleTemplates);
270 for (
int i = 0; i < numberOfFileIngestThreads; ++i) {
271 FileIngestPipeline pipeline =
new FileIngestPipeline(
this, fileIngestModuleTemplates);
272 this.fileIngestPipelinesQueue.put(pipeline);
273 this.fileIngestPipelines.add(pipeline);
275 }
catch (InterruptedException ex) {
281 Thread.currentThread().interrupt();
285 this.
addIngestModules(firstStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
286 this.
addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase);
287 this.
addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase);
289 logErrorMessage(Level.WARNING,
"Failed to add ingest modules listing to case database", ex);
293 private void addIngestModules(List<IngestModuleTemplate> templates, IngestModuleType type, SleuthkitCase skCase)
throws TskCoreException {
295 ingestModules.add(skCase.addIngestModule(module.getModuleName(), FactoryClassNameNormalizer.normalize(module.getModuleFactory().getClass().getCanonicalName()), type, module.getModuleFactory().getModuleVersionNumber()));
315 List<IngestModuleTemplate> templates =
new ArrayList<>();
316 for (String moduleClassName : pipelineConfig) {
317 if (ingestModuleTemplates.containsKey(moduleClassName)) {
318 templates.add(ingestModuleTemplates.remove(moduleClassName));
338 String getExecutionContext() {
347 Content getDataSource() {
357 boolean shouldProcessUnallocatedSpace() {
366 FilesSet getFileIngestFilter() {
375 boolean hasIngestPipeline() {
388 return (this.firstStageDataSourceIngestPipeline.isEmpty() ==
false);
398 return (this.secondStageDataSourceIngestPipeline.isEmpty() ==
false);
407 if (!this.fileIngestPipelines.isEmpty()) {
408 return !this.fileIngestPipelines.get(0).isEmpty();
418 List<IngestModuleError> start() {
420 if (errors.isEmpty()) {
423 }
catch (TskCoreException | NoCurrentCaseException ex) {
424 logErrorMessage(Level.WARNING,
"Failed to add ingest job info to case database", ex);
444 List<IngestModuleError> errors =
new ArrayList<>();
449 errors.addAll(this.firstStageDataSourceIngestPipeline.startUp());
450 errors.addAll(this.secondStageDataSourceIngestPipeline.startUp());
457 if (errors.isEmpty()) {
458 for (FileIngestPipeline pipeline : this.fileIngestPipelinesQueue) {
459 errors.addAll(pipeline.startUp());
460 if (!errors.isEmpty()) {
466 while (!this.fileIngestPipelinesQueue.isEmpty()) {
467 FileIngestPipeline startedPipeline = this.fileIngestPipelinesQueue.poll();
468 if (startedPipeline.isRunning()) {
469 List<IngestModuleError> shutDownErrors = startedPipeline.shutDown();
470 if (!shutDownErrors.isEmpty()) {
496 this.estimatedFilesToProcess = this.dataSource.accept(
new GetFilesCountVisitor());
524 logInfoMessage(
"Scheduling first stage data source and file level analysis tasks");
527 logInfoMessage(
"Scheduling first stage data source level analysis tasks");
530 logInfoMessage(
"Scheduling file level analysis tasks, no first stage data source level analysis configured");
557 logInfoMessage(
"Scheduling second stage data source level analysis tasks");
567 String displayName = NbBundle.getMessage(this.getClass(),
568 "IngestJob.progress.dataSourceIngest.initialDisplayName",
569 this.dataSource.getName());
570 this.dataSourceIngestProgress = ProgressHandle.createHandle(displayName,
new Cancellable() {
572 public boolean cancel() {
579 DataSourceIngestCancellationPanel panel =
new DataSourceIngestCancellationPanel();
580 String dialogTitle = NbBundle.getMessage(
DataSourceIngestJob.this.getClass(),
"IngestJob.cancellationDialog.title");
581 JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), panel, dialogTitle, JOptionPane.OK_OPTION, JOptionPane.PLAIN_MESSAGE);
582 if (panel.cancelAllDataSourceIngestModules()) {
590 this.dataSourceIngestProgress.start();
591 this.dataSourceIngestProgress.switchToIndeterminate();
602 String displayName = NbBundle.getMessage(this.getClass(),
603 "IngestJob.progress.fileIngest.displayName",
604 this.dataSource.getName());
605 this.fileIngestProgress = ProgressHandle.createHandle(displayName,
new Cancellable() {
607 public boolean cancel() {
616 this.fileIngestProgress.start();
617 this.fileIngestProgress.switchToDeterminate((
int) this.estimatedFilesToProcess);
629 switch (this.stage) {
651 List<IngestModuleError> errors =
new ArrayList<>();
652 while (!this.fileIngestPipelinesQueue.isEmpty()) {
653 FileIngestPipeline pipeline = fileIngestPipelinesQueue.poll();
654 if (pipeline.isRunning()) {
655 errors.addAll(pipeline.shutDown());
658 if (!errors.isEmpty()) {
666 if (this.dataSourceIngestProgress != null) {
667 this.dataSourceIngestProgress.finish();
668 this.dataSourceIngestProgress = null;
675 if (this.fileIngestProgress != null) {
676 this.fileIngestProgress.finish();
677 this.fileIngestProgress = null;
703 if (this.dataSourceIngestProgress != null) {
704 this.dataSourceIngestProgress.finish();
705 this.dataSourceIngestProgress = null;
709 if (ingestJob != null) {
710 if (this.cancelled) {
712 ingestJob.setIngestJobStatus(IngestJobStatusType.CANCELLED);
713 }
catch (TskCoreException ex) {
714 logErrorMessage(Level.WARNING,
"Failed to update ingest job status in case database", ex);
718 ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED);
719 }
catch (TskCoreException ex) {
720 logErrorMessage(Level.WARNING,
"Failed to update ingest job status in case database", ex);
724 this.ingestJob.setEndDateTime(
new Date());
725 }
catch (TskCoreException ex) {
726 logErrorMessage(Level.WARNING,
"Failed to set job end date in case database", ex);
729 this.parentJob.dataSourceJobFinished(
this);
738 void process(DataSourceIngestTask task) {
741 if (!this.isCancelled() && !this.currentDataSourceIngestPipeline.isEmpty()) {
742 List<IngestModuleError> errors =
new ArrayList<>();
743 errors.addAll(this.currentDataSourceIngestPipeline.process(task));
744 if (!errors.isEmpty()) {
756 if (null != this.dataSourceIngestProgress) {
757 this.dataSourceIngestProgress.finish();
758 this.dataSourceIngestProgress = null;
764 DataSourceIngestJob.taskScheduler.notifyTaskCompleted(task);
780 void process(FileIngestTask task)
throws InterruptedException {
782 if (!this.isCancelled()) {
783 FileIngestPipeline pipeline = this.fileIngestPipelinesQueue.take();
784 if (!pipeline.isEmpty()) {
785 AbstractFile file = task.getFile();
793 if (this.processedFiles <= this.estimatedFilesToProcess) {
794 this.fileIngestProgress.progress(file.getName(), (int) this.processedFiles);
796 this.fileIngestProgress.progress(file.getName(), (int) this.estimatedFilesToProcess);
798 this.filesInProgress.add(file.getName());
805 List<IngestModuleError> errors =
new ArrayList<>();
806 errors.addAll(pipeline.process(task));
807 if (!errors.isEmpty()) {
811 if (this.doUI && !this.cancelled) {
817 this.filesInProgress.remove(file.getName());
818 if (this.filesInProgress.size() > 0) {
819 this.fileIngestProgress.progress(this.filesInProgress.get(0));
821 this.fileIngestProgress.progress(
"");
826 this.fileIngestPipelinesQueue.put(pipeline);
829 DataSourceIngestJob.taskScheduler.notifyTaskCompleted(task);
841 void addFiles(List<AbstractFile> files) {
842 if (DataSourceIngestJob.Stages.FIRST ==
this.stage) {
843 DataSourceIngestJob.taskScheduler.fastTrackFileIngestTasks(
this, files);
845 logErrorMessage(Level.SEVERE,
"Adding files to job during second stage analysis not supported");
863 void updateDataSourceIngestProgressBarDisplayName(String displayName) {
864 if (this.doUI && !this.cancelled) {
866 this.dataSourceIngestProgress.setDisplayName(displayName);
879 void switchDataSourceIngestProgressBarToDeterminate(
int workUnits) {
880 if (this.doUI && !this.cancelled) {
882 if (null != this.dataSourceIngestProgress) {
883 this.dataSourceIngestProgress.switchToDeterminate(workUnits);
894 void switchDataSourceIngestProgressBarToIndeterminate() {
895 if (this.doUI && !this.cancelled) {
897 if (null != this.dataSourceIngestProgress) {
898 this.dataSourceIngestProgress.switchToIndeterminate();
910 void advanceDataSourceIngestProgressBar(
int workUnits) {
911 if (this.doUI && !this.cancelled) {
913 if (null != this.dataSourceIngestProgress) {
914 this.dataSourceIngestProgress.progress(
"", workUnits);
926 void advanceDataSourceIngestProgressBar(String currentTask) {
927 if (this.doUI && !this.cancelled) {
929 if (null != this.dataSourceIngestProgress) {
930 this.dataSourceIngestProgress.progress(currentTask);
944 void advanceDataSourceIngestProgressBar(String currentTask,
int workUnits) {
945 if (this.doUI && !this.cancelled) {
947 this.dataSourceIngestProgress.progress(currentTask, workUnits);
959 boolean currentDataSourceIngestModuleIsCancelled() {
969 void currentDataSourceIngestModuleCancellationCompleted(String moduleDisplayName) {
970 this.currentDataSourceIngestModuleCancelled =
false;
971 this.cancelledDataSourceIngestModules.add(moduleDisplayName);
982 this.dataSourceIngestProgress.finish();
983 this.dataSourceIngestProgress = null;
994 DataSourceIngestPipeline.PipelineModule getCurrentDataSourceIngestModule() {
995 if (null != this.currentDataSourceIngestPipeline) {
996 return this.currentDataSourceIngestPipeline.getCurrentlyRunningModule();
1006 void cancelCurrentDataSourceIngestModule() {
1007 this.currentDataSourceIngestModuleCancelled =
true;
1016 void cancel(IngestJob.CancellationReason reason) {
1017 this.cancelled =
true;
1019 DataSourceIngestJob.taskScheduler.cancelPendingTasksForIngestJob(
this);
1023 if (null != dataSourceIngestProgress) {
1024 dataSourceIngestProgress.setDisplayName(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.dataSourceIngest.initialDisplayName", dataSource.getName()));
1025 dataSourceIngestProgress.progress(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.cancelling"));
1030 if (null != this.fileIngestProgress) {
1031 this.fileIngestProgress.setDisplayName(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.fileIngest.displayName", this.dataSource.getName()));
1032 this.fileIngestProgress.progress(NbBundle.getMessage(
this.getClass(),
"IngestJob.progress.cancelling"));
1045 void setCurrentFileIngestModule(String moduleName, String taskName) {
1046 this.currentFileIngestModule = moduleName;
1047 this.currentFileIngestTask = taskName;
1056 boolean isCancelled() {
1065 IngestJob.CancellationReason getCancellationReason() {
1076 logger.log(Level.INFO, String.format(
"%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(),
id));
1088 logger.log(level, String.format(
"%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(),
id), throwable);
1099 logger.log(level, String.format(
"%s (data source = %s, objId = %d, jobId = %d)", message, dataSource.getName(), dataSource.getId(),
id));
1109 logErrorMessage(Level.SEVERE, String.format(
"%s experienced an error during analysis", error.getModuleDisplayName()), error.getThrowable());
1118 Snapshot getSnapshot(
boolean getIngestTasksSnapshot) {
1124 boolean fileIngestRunning =
false;
1125 Date fileIngestStartTime = null;
1127 for (FileIngestPipeline pipeline : this.fileIngestPipelines) {
1128 if (pipeline.isRunning()) {
1129 fileIngestRunning =
true;
1131 Date pipelineStartTime = pipeline.getStartTime();
1132 if (null != pipelineStartTime && (null == fileIngestStartTime || pipelineStartTime.before(fileIngestStartTime))) {
1133 fileIngestStartTime = pipelineStartTime;
1137 long processedFilesCount = 0;
1138 long estimatedFilesToProcessCount = 0;
1139 long snapShotTime =
new Date().getTime();
1140 IngestJobTasksSnapshot tasksSnapshot = null;
1142 if (getIngestTasksSnapshot) {
1146 snapShotTime =
new Date().getTime();
1148 tasksSnapshot = DataSourceIngestJob.taskScheduler.getTasksSnapshotForJob(
id);
1152 return new Snapshot(this.dataSource.getName(),
id,
createTime,
1153 getCurrentDataSourceIngestModule(), fileIngestRunning, fileIngestStartTime,
1155 processedFilesCount, estimatedFilesToProcessCount, snapShotTime, tasksSnapshot);
1161 public static final class Snapshot implements Serializable {
1183 Snapshot(String dataSourceName,
long jobId,
long jobStartTime, PipelineModule dataSourceIngestModule,
1184 boolean fileIngestRunning, Date fileIngestStartTime,
1186 long processedFiles,
long estimatedFilesToProcess,
1187 long snapshotTime, IngestJobTasksSnapshot tasksSnapshot) {
1188 this.dataSource = dataSourceName;
1191 this.dataSourceLevelIngestModule = dataSourceIngestModule;
1197 this.cancelledDataSourceModules = cancelledModules;
1201 this.snapShotTime = snapshotTime;
1211 long getSnapshotTime() {
1221 String getDataSource() {
1241 long getJobStartTime() {
1245 DataSourceIngestPipeline.PipelineModule getDataSourceLevelIngestModule() {
1249 boolean getFileIngestIsRunning() {
1253 Date getFileIngestStartTime() {
1264 return (
double) processedFiles / ((snapShotTime -
jobStartTime) / 1000);
1272 long getFilesProcessed() {
1282 long getFilesEstimated() {
1286 long getRootQueueSize() {
1287 if (null == this.tasksSnapshot) {
1290 return this.tasksSnapshot.getRootQueueSize();
1293 long getDirQueueSize() {
1294 if (null == this.tasksSnapshot) {
1297 return this.tasksSnapshot.getDirectoryTasksQueueSize();
1300 long getFileQueueSize() {
1301 if (null == this.tasksSnapshot) {
1304 return this.tasksSnapshot.getFileQueueSize();
1307 long getDsQueueSize() {
1308 if (null == this.tasksSnapshot) {
1311 return this.tasksSnapshot.getDsQueueSize();
1314 long getRunningListSize() {
1315 if (null == this.tasksSnapshot) {
1318 return this.tasksSnapshot.getRunningListSize();
1321 boolean isCancelled() {
1330 IngestJob.CancellationReason getCancellationReason() {
1341 List<String> getCancelledDataSourceIngestModules() {
1342 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