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