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 logger.log(Level.SEVERE,
"Failed to add ingest modules to 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 logger.log(Level.SEVERE,
"Failed to add ingest job to database.", ex);
427 logger.log(Level.INFO,
"Starting first stage analysis for {0} (jobId={1})",
new Object[]{dataSource.getName(), this.id});
430 logger.log(Level.INFO,
"Starting second stage analysis for {0} (jobId={1}), no first stage configured",
new Object[]{dataSource.getName(), this.id});
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 logger.log(Level.INFO,
"Scheduling first stage data source and file level analysis tasks for {0} (jobId={1})",
new Object[]{dataSource.getName(), this.id});
527 logger.log(Level.INFO,
"Scheduling first stage data source level analysis tasks for {0} (jobId={1}), no file level analysis configured",
new Object[]{dataSource.getName(), this.id});
530 logger.log(Level.INFO,
"Scheduling file level analysis tasks for {0} (jobId={1}), no first stage data source level analysis configured",
new Object[]{dataSource.getName(), this.id});
549 logger.log(Level.INFO,
"Starting second stage analysis for {0} (jobId={1})",
new Object[]{dataSource.getName(), this.id});
557 logger.log(Level.INFO,
"Scheduling second stage data source level analysis tasks for {0} (jobId={1})",
new Object[]{dataSource.getName(), this.id});
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) {
646 logger.log(Level.INFO,
"Finished first stage analysis for {0} (jobId={1})",
new Object[]{dataSource.getName(), this.id});
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;
696 logger.log(Level.INFO,
"Finished analysis for {0} (jobId={1})",
new Object[]{dataSource.getName(), this.id});
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 logger.log(Level.SEVERE,
"Failed to set ingest status for ingest job in database.", ex);
718 ingestJob.setIngestJobStatus(IngestJobStatusType.COMPLETED);
719 }
catch (TskCoreException ex) {
720 logger.log(Level.SEVERE,
"Failed to set ingest status for ingest job in database.", ex);
724 this.ingestJob.setEndDateTime(
new Date());
725 }
catch (TskCoreException ex) {
726 logger.log(Level.SEVERE,
"Failed to set end date for ingest job in 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 DataSourceIngestJob.logger.log(Level.SEVERE,
"Adding files during second stage 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 DataSourceIngestJob.
logger.log(Level.SEVERE, String.format(
"%s experienced an error analyzing %s (jobId=%d)", error.getModuleDisplayName(), dataSource.getName(), this.
id), error.getThrowable());
1085 Snapshot getSnapshot(
boolean getIngestTasksSnapshot) {
1091 boolean fileIngestRunning =
false;
1092 Date fileIngestStartTime = null;
1094 for (FileIngestPipeline pipeline : this.fileIngestPipelines) {
1095 if (pipeline.isRunning()) {
1096 fileIngestRunning =
true;
1098 Date pipelineStartTime = pipeline.getStartTime();
1099 if (null != pipelineStartTime && (null == fileIngestStartTime || pipelineStartTime.before(fileIngestStartTime))) {
1100 fileIngestStartTime = pipelineStartTime;
1104 long processedFilesCount = 0;
1105 long estimatedFilesToProcessCount = 0;
1106 long snapShotTime =
new Date().getTime();
1107 IngestJobTasksSnapshot tasksSnapshot = null;
1109 if (getIngestTasksSnapshot) {
1113 snapShotTime =
new Date().getTime();
1115 tasksSnapshot = DataSourceIngestJob.taskScheduler.getTasksSnapshotForJob(
id);
1119 return new Snapshot(this.dataSource.getName(),
id,
createTime,
1120 getCurrentDataSourceIngestModule(), fileIngestRunning, fileIngestStartTime,
1122 processedFilesCount, estimatedFilesToProcessCount, snapShotTime, tasksSnapshot);
1128 public static final class Snapshot implements Serializable {
1150 Snapshot(String dataSourceName,
long jobId,
long jobStartTime, PipelineModule dataSourceIngestModule,
1151 boolean fileIngestRunning, Date fileIngestStartTime,
1153 long processedFiles,
long estimatedFilesToProcess,
1154 long snapshotTime, IngestJobTasksSnapshot tasksSnapshot) {
1155 this.dataSource = dataSourceName;
1158 this.dataSourceLevelIngestModule = dataSourceIngestModule;
1164 this.cancelledDataSourceModules = cancelledModules;
1168 this.snapShotTime = snapshotTime;
1178 long getSnapshotTime() {
1188 String getDataSource() {
1208 long getJobStartTime() {
1212 DataSourceIngestPipeline.PipelineModule getDataSourceLevelIngestModule() {
1216 boolean getFileIngestIsRunning() {
1220 Date getFileIngestStartTime() {
1231 return (
double) processedFiles / ((snapShotTime -
jobStartTime) / 1000);
1239 long getFilesProcessed() {
1249 long getFilesEstimated() {
1253 long getRootQueueSize() {
1254 if (null == this.tasksSnapshot) {
1257 return this.tasksSnapshot.getRootQueueSize();
1260 long getDirQueueSize() {
1261 if (null == this.tasksSnapshot) {
1264 return this.tasksSnapshot.getDirectoryTasksQueueSize();
1267 long getFileQueueSize() {
1268 if (null == this.tasksSnapshot) {
1271 return this.tasksSnapshot.getFileQueueSize();
1274 long getDsQueueSize() {
1275 if (null == this.tasksSnapshot) {
1278 return this.tasksSnapshot.getDsQueueSize();
1281 long getRunningListSize() {
1282 if (null == this.tasksSnapshot) {
1285 return this.tasksSnapshot.getRunningListSize();
1288 boolean isCancelled() {
1297 IngestJob.CancellationReason getCancellationReason() {
1308 List<String> getCancelledDataSourceIngestModules() {
1309 return Collections.unmodifiableList(this.cancelledDataSourceModules);
transient final List< String > cancelledDataSourceModules
final List< AbstractFile > files
final boolean fileIngestRunning
transient final boolean jobCancelled
static synchronized IngestManager getInstance()
final long processedFiles
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
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